-- W A R N I N G : some invalid deprecated tags
/*******************************************************
** generated in the:17-12-14T16:11 by frenchi\stefano@FRENCHI from utility_new
** latest objs to 171214:sp__script
*******************************************************/
set nocount on
declare
    @db sysname,@svr sysname,
    @emsg nvarchar(2048),@esev int,@ests int

select @db=db_name(), @svr=@@servername

if @svr='FRENCHI\SQLEXPRESS0' and @db='utility'
    begin
    raiserror('Cannot execute from db where originated.',
              11,1) with nowait
    goto end_of_script
    end

-- check db version
if (select cmptlevel
    from master..sysdatabases
    where [name]=@db
    )<90
    begin
    raiserror('DB compatibility level must be at least 90 (MSSql2k5)',
              10,1) with nowait
    raiserror('Please run "exec sp_dbcmptlevel ''%s'', 90"',
              10,1,@db) with nowait
    raiserror('',11,1) with nowait
    goto end_of_script
    end



-- for versioning
declare
    @aut sysname,                   -- store author
    @ver decimal(14,4),             -- used to store new obj version
    @msg nvarchar(128),             -- generic message string
    @repeat int                     -- number of repeat of script
                                    -- if error of unknow obj, script is
                                    -- repeated bacause dependencies

if object_id('tempdb..#script_results') is null
    create table #script_results(
        id int identity,
        dt datetime default(getdate()) not null,
        who sysname default system_user+'@'+isnull(host_name(),'???'),
        grp sysname not null,
        rep int null,
        number int null,
        message nvarchar(2048) not null,
        severity int not null,
        state int null,
        line int null,
        [procedure] sysname null
        )

if object_id('tempdb..#script_catch') is null
    exec('
        create proc #script_catch @grp sysname,@repeat int
        as
        begin
        insert #script_results(dt,grp,rep,number,message,severity,state,line,[procedure])
        select getdate(),@grp,@repeat,
               error_number(),isnull(error_message(),''n/s''),
               error_severity(),error_state(),
               error_line(),error_procedure()
        end
    ')

select
    @repeat=1

begin_of_script:

-- ##########################
-- ##
-- ## disable trace db;
-- ## hope nobody work on important things while upgrade
-- ##
-- ########################################################
if not object_id('sp__script_trace_db') is null
    exec sp__script_trace_db 'uninstall'


-- ##########################
-- ##
-- ## core objects has different version check
-- ##
-- ########################################################

-- ============================================================= fn__script_sign
raiserror('drop&create "fn__script_sign"',10,1)
if exists(
    select top 1 null from sys.objects
    where name='fn__script_sign'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_sign]
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131123\s.zaglio: added default exclusion of charset&collation
    v:130730.1154,130729\s.zaglio: see comments into code
    v:130709\s.zaglio: changed to use hash
    v:130406\s.zaglio: added sign of jobs
    v:120821\s.zaglio: added sign of views
    v:120730\s.zaglio: added @detail 4 for sign of pure struct, without names
    v:120517\s.zaglio: removed core group
    v:120516\s.zaglio: return a checksum sign of current db object
    d:120516\s.zaglio: fn__crc32_table
    t:sp__script_sign_test
    t:select dbo.fn__script_sign(''fn__sysobjects'',1)
    t:select dbo.fn__script_sign(''fn__script_sign'',1)
*/
CREATE function fn__script_sign(@obj sysname = null,@detail tinyint = null)
returns numeric(14,4)
as
begin
declare
    @xtype varchar(2),@ver numeric(14,4),@obj_id int,
    @src nvarchar(max),@n int

select
    @obj_id=object_id(@obj),@detail=isnull(@detail,0),@src=''''

-- details for non jobs:
-- bit_val  meaning
-- 0/null   table columns or params of sp/fn, without defaults
-- 1        table columns with index or params of sp/fn with body
-- 2        table columns or params of sp/fn without names, without defaults
-- 4        table columns with index or params of sp/fn without names
-- details for jobs:
-- bit_val  meaning
-- 0/null   names/status of jobs
-- 1        names/status of jobs with names,commands,flags,outfile of steps
-- 2        status of jobs
-- 6        status of jobs with commands,flags,outfile of steps without names
if left(@obj,2)!=''j:''
    begin
    select @xtype=xtype from sysobjects where id=@obj_id
    if @xtype is null return null
    if @xtype in (''U'',''V'')
        begin
        select @src=@src+txt+char(13)
        from (
            select
                case when @detail&4=4 then '''' else column_name+''|'' end+
                cast(ordinal_position as varchar)+''|''+
                isnull(column_default,'''')+''|''+is_nullable+''|''+data_type+''|''+
                isnull(cast(character_maximum_length as varchar),'''')+''|''+
                isnull(cast(character_octet_length as varchar),'''')+''|''+
                isnull(cast(numeric_precision as varchar),'''')+''|''+
                isnull(cast(numeric_precision_radix as varchar),'''')+''|''+
                isnull(cast(numeric_scale  as varchar),'''')+''|''+
                isnull(cast(datetime_precision  as varchar),'''')+''|''+
                case when @detail&8=0 then ''''
                     else isnull(character_set_name,'''')+''|''+
                          isnull(collation_name,'''')
                end
                as txt
            -- select top 100 *
            from information_schema.columns with (nolock)
            where table_name=@obj
            ) tbl
        if @detail&1=1
        select @src=@src+txt+char(13)
        from (
            select
                -- consider only structure, not the position on ownership
                -- [filegroup]+''|''+
                case when @detail&4=4 then '''' else parentname+''|'' end+
                -- parentowner+''|''+
                case when @detail&4=4 then '''' else indexname+''|''+columnname+''|'' end+
                cast(descending as char)+''|''+cast(is_included_column as char)+''|''+
                cast([clustered] as char)+''|''+cast([unique] as char)+''|''+
                cast(uniqueconstraint as char)+''|''+cast([primary] as char)+
                cast([norecompute] as char)+''|''+cast(ignoredupkey as char)
                as txt,
                ord as ord
            from (
                -- declare @tbl sysname select @tbl=''log_ddl''
                select
                       sysfilegroups.groupname                                                    as [filegroup],
                       sysobjects.name                                                            as parentname,
                       sysusers.name                                                              as parentowner,
                       sysindexes.name                                                            as indexname,
                       syscolumns.name                                                            as columnname,
                       convert(bit,isnull(indexkey_property(syscolumns.id,sysindexkeys.indid,keyno,''isdescending''),
                                          0))                                                     as descending,
                       convert(bit,0)                                                             as is_included_column,
                       convert(bit,indexproperty(sysindexes.id,sysindexes.name,N''isclustered''))   as [clustered],
                       convert(bit,indexproperty(sysindexes.id,sysindexes.name,N''isunique''))      as [unique],
                       convert(bit,case
                                     when (sysindexes.status & 4096) = 0
                                     then 0
                                     else 1
                                   end) as uniqueconstraint,
                       convert(bit,case
                                     when (sysindexes.status & 2048) = 0
                                     then 0
                                     else 1
                                   end) as [primary],
                       convert(bit,case
                                     when (sysindexes.status & 0x1000000) = 0
                                     then 0
                                     else 1
                                   end) as [norecompute],
                       convert(bit,case
                                     when (sysindexes.status & 0x1) = 0
                                     then 0
                                     else 1
                                   end) as ignoredupkey,
                       sysindexes.name+''|''+right(''00000''+cast(sysindexkeys.keyno as varchar),5) as ord
                from     sysindexes with (nolock)                       -- select * from sysindexes where name=''ix_log_ddl''
                         inner join sysindexkeys with (nolock)          -- select * from sysindexkeys where indid=2 and id=2103730597
                           on sysindexes.indid = sysindexkeys.indid     -- select * from sys.indexes where name=''ix_log_ddl''
                              and sysindexkeys.id = sysindexes.id       -- select * from sys.indexkeys where indid=2 and id=2103730597
                         inner join syscolumns with (nolock)            -- select * from syscolumns where id=2103730597 and colid in (1,6,12,4)
                           on syscolumns.colid = sysindexkeys.colid     -- select * from sys.index_columns where object_id=2103730597 and index_id=2
                              and syscolumns.id = sysindexes.id
                         inner join sysobjects with (nolock)
                           on sysobjects.id = sysindexes.id
                         left join sysusers with (nolock)
                           on sysusers.uid = sysobjects.uid
                         left join sysfilegroups with (nolock)
                           on sysfilegroups.groupid = sysindexes.groupid
                where    (objectproperty(sysindexes.id,''istable'') = 1
                           or objectproperty(sysindexes.id,''isview'') = 1)
                         and objectproperty(sysindexes.id,''issystemtable'') = 0
                         and indexproperty(sysindexes.id,sysindexes.name,N''isautostatistics'') = 0
                         and indexproperty(sysindexes.id,sysindexes.name,N''ishypothetical'') = 0
                         and sysindexes.name is not null
                         and sysobjects.name=@obj
                ) cols
            ) tbl
        end -- table sign
    else    -- if job
        begin
        if @detail&1=0
            select @src=@src+txt+char(13)
            from (
                select
                    case when @detail&4=0 then specific_schema+''|'' else '''' end+
                    cast(ordinal_position as varchar)+''|''+
                    parameter_mode+''|''+
                    case when @detail&4=0 then parameter_name+''|'' else '''' end+
                    data_type+''|''+
                    isnull(cast(character_maximum_length as varchar),'''')+''|''+
                    isnull(cast(character_octet_length as varchar),'''')+''|''+
                    isnull(cast(numeric_precision as varchar),'''')+''|''+
                    isnull(cast(numeric_precision_radix as varchar),'''')+''|''+
                    isnull(cast(numeric_scale  as varchar),'''')+''|''+
                    isnull(cast(datetime_precision  as varchar),'''')+''|''+
                    isnull(character_set_name,'''')+''|''+isnull(collation_name,'''')
                    as txt
                -- select top 100 *
                from information_schema.parameters with (nolock)
                where specific_name=@obj
                ) tbl
        else
            begin
            select top 1 @src=left(definition,4000)
            from sys.sql_modules with (nolock)
            where object_id=@obj_id
            declare @p1 int,@p2 int, @p3 int
            select @p1=patindex(''%[vr]:%'',@src)
            if @p1>0
                begin
                select @src=substring(@src,@p1+2,128),@p1=1
                select @p2=patindex(''%[,\]%'',@src)
                select @src=substring(@src,@p1,@p2-@p1)
                select @ver=cast(@src as numeric(10,4))
                end
            end
        end -- any other obj
    end -- non jobs

else

    begin
    -- select dbo.fn__script_sign(''j:%'',default) -- tested&verified
    -- select dbo.fn__script_sign(''j:%'',2) -- tested&verified
    -- select dbo.fn__script_sign(''j:%'',1) -- tested&verified
    -- select dbo.fn__script_sign(''j:%'',6) -- tested
    select @obj=substring(@obj,3,128)
    select @src=@src+txt+char(13)
    from (
        select
            case when @detail&2!=2 then name+''|'' else '''' end+
            cast(enabled as char(1)) as txt
        from msdb..sysjobs
        where name like @obj
        -- nb: the order do not influence the result
        ) jobs

    if @detail&1=1 or @detail&4=4
        select @src=@src+txt+char(13)
        from (
            select top 100 percent
                len(command) lcmd,
                case when @detail&4!=4 then step_name+''|'' else '''' end+
                command+''|''+cast(flags as nvarchar(5))+''|''+
                isnull(output_file_name,'''') as txt
            from msdb..sysjobs j
            join msdb..sysjobsteps js on j.job_id=js.job_id
            where j.name like @obj
            ) jobs

    end -- jobs

/* 130730: it looks like source code is different from case and case, so
           the source hash is not correct with the effect that destination
           is continously recompiled; so I converted sign of vi/fn/sp
           back to get first r/v tag and the rest is returned as crc32
           that fit the numeric integer part of version                       */

if @ver is null
    begin
    declare @chk int select @chk=0
    ;with a as (select 1 as n union all select 1) -- 2
         ,b as (select 1 as n from a ,a a1)       -- 4
         ,c as (select 1 as n from b ,b b1)       -- 16
         ,d as (select 1 as n from c ,c c1)       -- 256
         ,e as (select 1 as n from d ,d d1)       -- 65,536
         ,f as (select 1 as n from e ,e e1)       -- 4,294,967,296=17+trillion chrs
         ,factored as (select row_number() over (order by n) rn from f)
         ,factors as (select rn,(rn*4000)+1 factor from factored)

    select @chk = @chk ^
                 checksum(
                    substring(
                        cast(@src as varbinary(max)),
                        factor - 4000,
                        4000
                        )
                    )
    from factors
    where rn <= ceiling(datalength(cast(@src as varbinary(max)))/(4000.0))
    select @ver=@chk
    end

return @ver
end -- fn__script_sign'

-- ======================================================================== tids
select @ver=dbo.fn__script_sign('tids',1)
if not @ver is null
and @ver=108162603.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'tids') with nowait
    goto skip_tids
    end

raiserror('re-creating "%s.%s"',10,1,@db,'tids') with nowait

if exists(
    select top 1 null from sys.objects
    where name='tids'

    and schema_id=schema_id('dbo')
    )
    drop view [dbo].[tids]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this for other app
    l:see LICENSE file
    g:obj,utility
    v:131006\s.zaglio: updated
    v:120517\s.zaglio: generic types
*/
CREATE view [tids] as
select
    0 as free,      -- the record can be replaced or id reused
    1 as cnt,       -- counter
    2 as tid ,      -- type id masker
    3 as flg ,      -- flags definition
    4 as seq ,      -- sequence type
    5 as mem ,      -- memory
    6 as obj ,      -- generic object
    7 as usr ,      -- user name
    8 as host,      -- host name
    9 as srv ,      -- server (131006\s.zaglio: modified from svr)
    10 as app ,     -- application
    11 as ev  ,     -- event
    12 as code,     -- code/programming/line of text
    13 as tsk ,     -- task, job, activity, actions, etc.
    14 as range,    -- range values
    15 as grp ,     -- used for groups, lists, etc.
    16 as db  ,     -- database
    17 as email,    -- email record
    18 as html,     -- html record
    19 as body,     -- body of email or html
    20 as iospc,    -- i/o specific
    21 as ioseg,    -- i/o specific segment
    22 as url,      -- unified resource locator
    23 as [file],   -- file name
    24 as [dir],    -- dir for file (parent); the path is the url
    25 as [prop],   -- property
    -1 as [tid.last]-- (131006\s.zaglio: modified from tids.last)'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_tids:


-- ======================================================================= flags
select @ver=dbo.fn__script_sign('flags',1)
if not @ver is null
and @ver=1413502072.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'flags') with nowait
    goto skip_flags
    end

raiserror('re-creating "%s.%s"',10,1,@db,'flags') with nowait

if exists(
    select top 1 null from sys.objects
    where name='flags'

    and schema_id=schema_id('dbo')
    )
    drop view [dbo].[flags]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this for other app
    l:see LICENSE file
    g:obj,utility
    v:131006\s.zaglio: updated
    v:120517\s.zaglio: global flags
*/
CREATE view [flags]
as
select
    1 as A,
    2 as B,
    4 as C,
    8 as D,
   16 as E,
   32 as F,
   64 as G,
  128 as H,
  256 as I,
  512 as J,
 1024 as K,
 2048 as L,
 4096 as M,
 8192 as N,
16384 as O,
32768 as P,

 512 as srv , -- concerning the server
1024 as db  , -- concerning the database
2048 as ddl , -- concerning Data Definition Language
 512 as ver , -- 1=version/0=release flag
 512 as RA  , -- replaces accented
  48 as type, -- segment type
   0 as SEG , -- generic
  32 as #FLT, -- filter
  16 as #GRP, -- group
  48 as #COM, -- comment
 512 as RPT , -- repeat
1024 as HDR , -- header
2048 as DTL , -- detail
4096 as FTR , -- footer
8192 as SCR                             , -- scramble

-- iof
  32 as [iof.wrong],
  64 as [iof.processed],

-- fn__script_sysobjs and ex fn__script_events
  16 as [sys],      -- system
  32 as [tmp],      -- temporary
  64 as [exclude],  -- eXclude

-- sp__dir, sp__ftp and files
  32 as [files.directory],
  64 as [files.download],
 128 as [files.upload],
 256 as [files.delete],
 512 as [files.ok],
1024 as [files.err],

  -1 as [flags.last]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_flags:


-- ===================================================================== fn__sym
select @ver=dbo.fn__script_sign('fn__sym',1)
if not @ver is null
and @ver=130614.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'fn__sym') with nowait
    goto skip_fn__sym
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sym') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sym'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sym]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130614\s.zaglio: added _ to non word and sentence
    v:130612\s.zaglio: added bounds,non_word,non_sentence
    v:120517\s.zaglio: removed from core group
    v:120201\s.zaglio: added to core group
    v:111222\s.zaglio: added gcs,gce,lcc
    v:111111\s.zaglio: added space
    v:100404\s.zaglio: added return values
    v:100328\s.zaglio: changed to table function and expanded to more symbols replace old fn__crlf,fn__seps
    v:091027\s.zaglio: for simplicity
    t:
        sp__find ''fn__crlf'' sp__find ''fn__seps''
        sp__find ''char(13)'' sp__find ''char(10)'' sp__find ''char(9)''
    todo: replace all char(13),char(10),char(9)
*/
CREATE function [dbo].[fn__sym]()
returns table
as
return
    select
        -- classic txt symbols
        char(ascii('' '')) [space],
        char(13) cr,char(10) lf,char(13)+char(10) crlf,char(9) tab,
        -- path separators
        ''\'' psep, ''/'' usep,
        -- returns values
        0 ok, -1 help,
        -- group comment start and end
        ''/*'' gcs, ''*/'' gce,
        -- line code commend
        ''--'' lcc,
        -- bounds of a (t)sql word
        ''s''''"_*+/,.;:\<>()=¬''+char(13)+char(10)+'' ''+char(9) bounds,
        ''%[^a-z0-9_]%'' non_word,
        ''%[^a-z0-9 _]%'' non_sentence
-- end fn__sym'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sym:


-- =============================================================== fn__str_parse
select @ver=dbo.fn__script_sign('fn__str_parse',1)
if not @ver is null
and @ver=121226.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'fn__str_parse') with nowait
    goto skip_fn__str_parse
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_parse') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_parse'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_parse]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:121226\s.zaglio: remake
    c:http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings
    v:121004\s.zaglio: remake using patindex and removed a bug
    v:120517\s.zaglio: removed from core group
    v:110630\s.zaglio: added to core group
    v:100418\s.zaglio: a bug near out of indx
    v:100221\S.Zaglio: return piece of string separated by one of char in @sym
    t:
        print isnull(dbo.fn__str_parse(''drive:\dir\subdir\file.ext'',''\.'',0),''?'') -- ?
        print dbo.fn__str_parse(''drive:\dir\subdir\file.ext'',''\.'',1) -- drive:
        print dbo.fn__str_parse(''drive:\dir\subdir\file.ext'',''\.'',2) -- dir
        print dbo.fn__str_parse(''drive:\dir\subdir\file.ext'',''\.'',3) -- subdir
        print dbo.fn__str_parse(''drive:\dir\subdir\file.ext'',''\.'',4) -- file
        print dbo.fn__str_parse(''drive:\dir\subdir\file.ext'',''\.'',5) -- ext
        print isnull(dbo.fn__str_parse(''drive:\dir\subdir\file.ext'',''\.'',6),''?'') -- ?
        select dbo.fn__str_parse(''a;b;'','';,'',1),dbo.fn__str_parse(''a;b;'','';,'',2) -- a,b
        select dbo.fn__str_parse(''a;b;'','';,'',3),dbo.fn__str_parse(''a;b;'','';,'',4) -- '''',null
*/
CREATE function [dbo].[fn__str_parse](
    @tokens nvarchar(4000),
    @sym nvarchar(32),
    @pos int
    )
returns nvarchar(4000)
as
begin
declare @token nvarchar(4000)
;with
    e1(n)        as (select 1 union all select 1 union all select 1 union all select 1
                     union all select 1 union all select 1 union all select 1
                     union all select 1 union all select 1 union all select 1),
    e2(n)        as (select 1 from e1 a, e1 b),
    e4(n)        as (select 1 from e2 a, e2 b),
    e42(n)       as (select 1 from e4 a, e2 b),
    ctetally(n)  as (select 0 union all select top (datalength(isnull(@tokens,1)))
                     row_number() over (order by (select null)) from e42),
    ctestart(n1) as (select t.n+1 from ctetally t
                     where (substring(@tokens,t.n,1) like ''[''+@sym+'']'' or t.n = 0)),
limits(n1,n2) as (
    select
        s.n1,
        isnull((select top 1 n1 from ctestart e where e.n1>s.n1 order by 1),len(@tokens)+2) n2
    from ctestart s
    ),
tokens(pos,token) as (
    select
        pos=row_number() over (order by (select null)),
        token=substring(@tokens,n1,n2-n1-1)
    from limits
    )
select @token=token
from tokens
where pos=@pos
return @token
end -- [fn__str_parse]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_parse:


-- ======================================================== fn__script_info_tags
select @ver=dbo.fn__script_sign('fn__script_info_tags',1)
if not @ver is null
and @ver=140119.1000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'fn__script_info_tags') with nowait
    goto skip_fn__script_info_tags
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_info_tags') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_info_tags'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_info_tags]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    todo:manage multiline tags;manage /**/ into ''''
    v:140119.1000\s.zaglio:again syntax near tags of 0 lenght
    v:140113.1002\s.zaglio\z.aglio: better syntax parses
    v:131103\s.zaglio:converted @row into @lvl and row into row of code
    v:131129\s.zaglio:added maxrecurtion to 1st with
    v:131126\s.zaglio:added management of r3;r2;r1 and c3;c2;c1
    v:130907\s.zaglio:added tag #
    v:130602\s.zaglio:added management of r3,r2,r1 in comments
    v:130528\s.zaglio:better end comments finding and used maxrecursion
    v:130523\s.zaglio:a bug where more spaces after create
    v:130522\s.zaglio:resolved bug near lf and changed results to nvarchar
    v:130511;121206\s.zaglio:a remake using with;added monoline tag S (scope) and J (job)
    v:120724,120517\s.zaglio:added tag x;removed from core group
    v:120510,111229\s.zaglio:a bug when comments greather than 4k;a small bug
    v:111222\s.zaglio:limited to /* ... */ with backward compatibility
    v:110630\s.zaglio:added to core grp
    v:110628,110624\s.zaglio:added deprecated;vesioned
    r:110614\s.zaglio:used only fn__str_parse;called from fn__script_info,sp__trace
    t:sp__script_info_tags_test @dbg=1
    t:select object_name(obj_id) name,* from fn__script_info(null,default,default)
    t:select object_name(obj_id) name,* from fn__script_info(null,''g'',default)
    t:select * from dbo.fn__script_info(''fn__script_info_tags'',''rv#'',0)
    t:select * from dbo.fn__script_info(''fn__script_info_tags'',''rv#'',default)
    t:select object_name(obj_id) obj,* from fn__script_info(null,''d'',default) -- deprecard
    t:sp__Script_store @opt=dis
-- c:old style
*/
CREATE function fn__script_info_tags(
    @buf nvarchar(max)=null,                -- top comment of sp,fn,v,etc.
    @grps sysname=null,
    @lvl tinyint=null                       -- row 0 is top of group,cmd pos
    )
returns @tag table (
    tag nvarchar(8),                        -- l,g,v,d,s,todo,#
    row smallint,                           -- row of code
    val1 nvarchar(4000),                    -- date(rvd),grp(g),cmd,cmt(c)
    val2 nvarchar(4000) null,               -- user,2nd grp,object type
    val3 nvarchar(4000) null,               -- comment,3rd grp,object
    status smallint null                    -- status of syntax of comments
)
as
begin

-- ============================================================= declarations ==

declare @lines table (
    pos int primary key,
    tag nvarchar(8),
    line nvarchar(4000),
    pslash smallint null,
    pcolon smallint null,
    pcom1 smallint null,
    pcom2 smallint null,
    pcom3 smallint null,
    ln smallint
    )

declare
    @line nvarchar(4000),
    @cr nchar(1),@lf nchar(1),@crlf nchar(2),
    @tab nchar(1),@i int,@j int,@k int,@st sysname,
    @pcmd int, @cmd sysname, @otype sysname, @oname sysname,
    @kind sysname,@tck varchar(16),@c nchar,@dbgt xml;

select
    @grps=isnull(@grps,''lkbvrjgbsctodo#''),  -- sp__Style
    @cr=cr,@lf=lf,@crlf=crlf,@tab=tab
from fn__sym()

-- ===================================================================== body ==

-- if @dbg=1 insert @tag(row,tag,val1,val2,val3) select 62,''dbg'',null,null,null

/*  extract create/alter [type] and object name
    proc
    func
    view
    tabl
    syno
    trig
*/

-- search correct create/alter proc/func/...
-- skip initial comments /*...*/ and /*../*..*/..*/
select @kind=''[pfvts][ruiay][onebi][cwlog]''
select @k=patindex(''%create %''+@kind+''%'',@buf)
if @k=0 select @k=patindex(''%alter %''+@kind+''%'',@buf)
select @i=charindex(''/*'',@buf),@j=charindex(''*/'',@buf,@i+2)
if @i>0 and @k>@i
    begin
    while @i<@j
        begin
        select @i=charindex(''/*'',@buf,@i+2)
        if @i=0 or (@i>@j and @i>@k) break
        if @i<@j select @j=charindex(''*/'',@buf,@j+2)
        end
    select @j=@j+2
    if @k>@j select @j=@k
    end
else
    select @j=@k
-- @j contain the last closed comment or 0
-- must be skipped blank lines and -- comment
-- strip headers and search for create/alter proc/func/view/trigger/synonym
select @line=substring(@buf,@j,256)
select @pcmd=patindex(''%create %''+@kind+''%'',@line)
if @pcmd=0 select @pcmd=patindex(''%alter %''+@kind+''%'',@line)
if @pcmd!=0
    begin
    -- extract declaration line and move all into single line
    select @line=replace(substring(@line,@pcmd,200),@tab,'' '')
    select @pcmd=@pcmd+@j+2 -- absolute position
    select @line=replace(replace(replace(@line,@crlf,@cr),@lf,@cr),@cr,'' '')
    -- reduce spaces
    while (charindex(''  '',@line)>0) select @line=replace(@line,''  '','' '')
    while (charindex(''. '',@line)>0) select @line=replace(@line,''. '',''.'')
    while (charindex('' .'',@line)>0) select @line=replace(@line,'' .'',''.'')
    while (charindex(''[ '',@line)>0) select @line=replace(@line,''[ '',''['')
    while (charindex('' ]'',@line)>0) select @line=replace(@line,'' ]'','']'')
    end

-- parse alter/create instruction
;with splits(pos, start, [stop])
as (
  select 1, 1, charindex('' '', @line)
  union all
  select
    pos + 1,
    [stop] + 1,
    charindex('' '', @line, [stop] + 1)
  from splits
  where [stop] > 0
)
,tokens as (
    select pos,
      ltrim(
        substring(
            @line,
            start,
            case when [stop] > 0 then [stop]-start else 256 end
            )
       ) as token
    from splits
)
select
    @cmd=case when pos=1 then token else @cmd end,
    @otype=case when pos=2 then token else @otype end,
    @oname=case when pos=3 then token else @oname end
from tokens
where pos<4
option (maxrecursion 1000)

select @i=charindex(''('',@oname)
if @i>0 select @oname=left(@oname,@i-1)
select @oname=isnull(quotename(parsename(@oname,2))+''.'','''')
             +quotename(parsename(@oname,1))

-- reduce to header to not occur into max occurence of 1000
select @buf=substring(@buf,1,@pcmd)

-- correct unix \n to uncorrect \n windows style and tab to space
select @buf=replace(replace(replace(@buf,@crlf,@cr),@lf,@cr),@tab,'' '')

/* debug session
-- select * from dbo.fn__script_info(''fn__script_info_tags'',default,0)
insert @tag(row,tag,val1,val2,val3)
select 75,''dbg'',@buf,null,null
*/

-- split top comment into lines
;with splits(pos, start, [stop])
as (
  select 1, cast(1 as int), charindex(@cr, @buf)
  union all
  select
    pos + 1,
    cast([stop] + (datalength(@cr)/2) as int),
    charindex(@cr, @buf, [stop] + (datalength(@cr)/2))
  from splits
  where [stop] > 0
)
,lines as (
    select pos,
      ltrim(
        substring(
            @buf,
            start,
            case when [stop] > 0 then [stop]-start else 4000 end
            )
       ) as line
    from splits
)
/*
    unfortunatelly a more sub withs cannot be used for unknown problemi with
    substring or left or charindex due some inner optimization of the engine
*/
,tags as (
    select pos,
           case
           when charindex('':'',line)>0
           then left(line,charindex('':'',line)-1)
           else ''''
           end as tag,
           case
           when charindex('':'',line)>0
           then ltrim(rtrim(substring(line,charindex('':'',line)+1,4000)))
           else ltrim(rtrim(line))
           end as line
    from lines
    )
,epured as (
    select pos,ltrim(replace(tag,''--'','''')) as tag/*old line style */,line
    from tags
    )
insert @lines(pos,tag,line,pslash,pcolon,pcom1,pcom2,pcom3,ln)
select
    pos,left(tag,8),line,
    nullif(isnull(
        nullif(isnull(
            nullif(charindex(''\'',line),0),
            charindex(''/'',line)),0),
        case -- case v:yyyymmaa:{space}comment
        when charindex('' '',line)<charindex('':'',line)
        then charindex('' '',line)
        end),0),
    nullif(charindex('':'',line),0),
    nullif(charindex('','',line),0),
    nullif(charindex('','',line,nullif(charindex('','',line),0)+1),0),
    nullif(charindex('','',line,nullif(charindex('','',line,
        nullif(charindex('','',line),0)+1),0)+1),0),
    len(line)
from epured
where left(line,2)!=''/*'' and right(line,2)!=''*/''
and (charindex(tag,@grps)>0 or @grps is null)
option (maxrecursion 1000)

-- code for debug
select @dbgt = (select * from @lines for xml auto)

/* debug session
-- select * from dbo.fn__script_info(''fn__script_info_tags'',default,default)
insert @tag(row,tag,val1,val2,val3) select 158,''dbg'',pos,tag,line from @lines
return
*/

-- extract group components
;with tags as (
    select
        tag,
        row_number() over(partition by tag order by pos)-1 as lvl,
        ltrim(rtrim(substring(line,1,isnull(pcom1-1,ln)))) as val1,
        ltrim(rtrim(substring(line,pcom1+1,isnull(pcom2-1,ln)-pcom1))) as val2,
        ltrim(rtrim(substring(line,pcom2+1,isnull(pcom3-1,ln)-pcom2))) as val3,
        pos
    from @lines
    where tag in (''g'',''s'') and ln>0
)
insert @tag(tag,row,val1,val2,val3)
select tag,pos,val1,val2,val3
from tags
where @lvl is null or @lvl=lvl
option (maxrecursion 1000)

-- extract revisions components
;with tags as (
    select
        tag,
        row_number()
        over(
            -- row 0 of a revision is the 1st r or v
            partition by case tag when ''r'' then ''v'' else tag end
            order by pos
            )-1
        as lvl,
        pos as row,
        ltrim(rtrim(substring(line,1,isnull(pslash-1,isnull(pcolon-1,ln)))))
        as val1,
        ltrim(rtrim(substring(line,pslash+1,isnull(pcolon-1,ln)-pslash)))
        as val2,
        ltrim(rtrim(substring(line,pcolon+1,ln-pcolon)))
        as val3
    from @lines
    where tag in (''v'',''r'',''d'',''x'',''o'') and ln>0
)
insert @tag(tag,row,val1,val2,val3)
select tag,row,val1,val2,val3
from tags
where @lvl is null or @lvl=lvl
option (maxrecursion 1000)

-- select * from fn__script_info(''fn__script_info_tags'',''rv'',default)

-- reduce r3,r2,r1 or r3;r2;r1 into r3; reduce c3;c2;c1 into c3
update @tag set
    val1=left(val1,isnull(nullif(charindex('','',val1),0),charindex('';'',val1))-1),
    val3=left(val3,isnull(nullif(charindex('';'',val3),0),len(val3)+1)-1)
where tag in (''r'',''v'')
and (charindex('','',val1)>0 -- ??and charindex('','',val1,charindex('','',val1))>0??
     or charindex('';'',val1)>0)

-- extract single tag component
;with tags as (
    select tag,
           row_number() over(partition by tag order by pos)-1 as lvl,
           line as val1,
           pos
    from @lines
    where tag in (''l'',''c'',''t'',''k'',''todo'')
)
insert @tag(tag,row,val1)
select tag,pos,val1
from tags
where @lvl is null or @lvl=lvl
option (maxrecursion 1000)

if charindex(''#'',@grps)>0
    begin
    -- if isnull(@pcmd,0)=0 select @i=''create/alter not found''
    insert @tag(tag,row,val1,val2,val3)
    select ''#'',@pcmd,@cmd,@otype,@oname
    end

-- apply convert into datetime and adjustments
return
end -- fn__script_info_tags'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_info_tags:


-- ============================================================= fn__script_info
select @ver=dbo.fn__script_sign('fn__script_info',1)
if not @ver is null
and @ver=140124.1000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'fn__script_info') with nowait
    goto skip_fn__script_info
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_info') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_info'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_info]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    todo:manage multiline tags
    v:140124.1000\s.zaglio:removed limits near comment parse
    v:131103.1000\s.zaglio:adapted to new fn__script_info_tags
    v:130925\s.zaglio:a bug near with (...datalength(''|'')/2...)
    v:130907\s.zaglio:adapted to fn__script_into_tags
    v:130528\s.zaglio:removed dep from fn__str_table_fast and fn__comments
    v:130523.1200\s.zaglio:a bug when @objs was null and added xt fld and minor other
    v:130522\s.zaglio:no more compatible with mssql2k and optimized @obj to @objs
    v:120517\s.zaglio:removed from core group
    v:110629.1600\s.zaglio:added obj name to output and isolted from fn__ismssql2k
    v:110628\s.zaglio:added info about db trigger
    v:110627\s.zaglio:versioned
    r:110314\s.zaglio:added use of fn__script_info_tags
    r:100523\s.zaglio:a bug into row value
    r:100518\s.zaglio:managed line commented tags
    r:100417\s.zaglio:return info contained in tag of header
    c:##########################################################
    c:########## CORE FUNCTION, DO NOT CALL OTHERs #############
    c:##########################################################
    t:sp__usage ''fn__Script_info''
    t:
        -- multiline t (todo: manage this sub-tag)
        select * from fn__script_info(''sp__style'',default,default)
        select * from fn__script_info(''fn__script_info'',default,default)
        select * from fn__script_info(''fn__script_info'',''rv'',default)
        select * from fn__script_info(''fn__script_info'',''r'',0)
    t:select object_name(obj_id) name,* from fn__script_info(null,default,default)
    t:select object_name(obj_id) name,* from fn__script_info(null,''g'',default)
    t:select * from dbo.fn__script_info(''sp__script_group'',''vr'',default)
    t:select * from dbo.fn__script_info(''tr__script_trace_db'',''gvr'',default)
    t:select * from dbo.fn__script_info(''fn__script_info|sp__util_%'',default,default)
    t:select * from dbo.fn__script_info(''unknown'',default,default)
    t:select * from dbo.fn__script_info(''tst_test'',default,default)
-- c:old style
*/
CREATE function [dbo].[fn__script_info](
    @objs sysname=null,                     -- all sp,fn,v,etc.
    @grps sysname=null,
    @lvl tinyint=null                       -- tag level (0 is top of group)
    )
returns @t table (
    obj_id int,
    obj sysname,
    xt varchar(2) null,
    tag nvarchar(4) null,                   -- l,g,v,d
    row smallint null,                      -- row of code
    val1 nvarchar(4000) null,               -- date,grp,deprecated
    val2 nvarchar(4000) null,               -- user,comment,2nd grp
    val3 nvarchar(4000) null                -- 3rd grp
)
as
begin

declare @r int,@id int,@buf nvarchar(max),@obj sysname
declare @objects table (id int,obj sysname,xt varchar(2),nfo nvarchar(256))

-- get objects
;with pieces(pos, start, [stop]) as (
  select 1, 1, charindex(''|'', @objs)
  union all
  select
    pos + 1,
    [stop] + 1,
    charindex(''|'', @objs, [stop] + 1)
  from pieces
  where [stop] > 0
),
splits as (
    select pos,
      substring(
        @objs,
        start,
        case when [stop] > 0 then [stop]-start else 4000 end
        ) as token
    from pieces
)
-- ;with splits as (select 1 as pos,@objs as token)
insert @objects (id,obj,xt)
select id,[name],xtype
from sysobjects o
cross apply splits
where (o.name like replace(token,''_'',''[_]'') or @objs is null)
and xtype in (''P'',''V'',''TF'',''FN'',''IF'',''FI'',''TR'')

union

-- special cases for db triggers
select object_id as id,[name],''TD''
from sys.triggers o
cross apply splits
where (o.name like replace(token,''_'',''[_]'') or @objs is null)
and parent_id=0 -- means db trigger

-- fill tag info (one or more per object)
insert @t(obj_id,obj,xt,row,tag,val1,val2,val3)
select id,obj,xt,row,tag,val1,val2,val3
from @objects o
join sys.sql_modules m on m.object_id=o.id
cross apply fn__script_info_tags([definition],@grps,@lvl) t

-- add not tagged objects
if not @grps in (''d'')
    insert @t(obj_id,obj,xt)
    select o.id,o.obj,o.xt
    from @objects o
    left join @t t on o.id=t.obj_id
    where t.obj_id is null

return
end -- fn__script_info'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_info:


-- ============================================================= fn__script_drop
select @ver=dbo.fn__script_sign('fn__script_drop',1)
if not @ver is null
and @ver=131001.1100
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'fn__script_drop') with nowait
    goto skip_fn__script_drop
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_drop') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_drop'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_drop]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:get,create,drop,script,if,exists
    v:131001.1100\s.zaglio: refined drop of pk
    r:130908\s.zaglio: return drop scripts
    t:sp__sysobjects_test
*/
CREATE function fn__script_drop(
    @obj sysname,
    @typ nvarchar(2),
    @schema sysname,
    @parent sysname,
    @opt sysname
    )
returns table
as
return
/*
AF = funzione di aggregazione (CLR)
C = vincolo CHECK
D = DEFAULT (vincolo o valore autonomo)
F = vincolo FOREIGN KEY
FN = funzione scalare SQL
FS = Funzione scalare di assembly (CLR)
FT = funzione valutata a livello di tabella assembly (CLR)
IF = funzione SQL inline valutata a livello di tabella
IT = tabella interna
P = Stored procedure SQL
PC = Stored procedure di assembly (CLR)
PG = Guida di piano
PK = vincolo PRIMARY KEY
R = regola (tipo obsoleto, autonoma)
RF = procedura-filtro-replica
S = tabella di base di sistema
SN = sinonimo
SQ = coda di servizio
TA = trigger DML assembly (CLR)
TF = funzione valutata a livello di tabella SQL
TR = trigger DML SQL
TT = tipo tabella
U = tabella (definita dall''utente)
UQ = vincolo UNIQUE
V = vista
X = stored procedure estesa
*/
select
    @obj as [obj],
    @typ as [type],
    @schema as [schema],

    drop_script=
        case when @typ=''PK''
             then ''alter table ''+
                  isnull(q_schema+''.'','''')+q_parent+
                  '' drop constraint ''+quotename(@obj)
        else
            ''drop ''+
            case when @typ in (''PK'',''UQ'',''F'')
                 then ''index ''+isnull(q_schema+''.'','''')
                              +isnull(q_parent+''.'','''')
                              +q_obj
            else
                case
                when @typ in (''FN'',''TF'',''IF'')
                then ''function ''
                when @typ in (''TR'',''TD'')
                then ''trigger ''
                when @typ=''SN''
                then ''synonym ''
                when @typ=''U''
                then ''table ''
                when @typ=''P''
                then ''proc ''
                when @typ=''V''
                then ''view ''
                when @typ=''D''
                then ''constraint ''
                when @typ in (''FN'',''IF'',''TF'')
                then ''function ''
                else ''#!unktype ''
                end+
                isnull(q_schema+''.'','''')+q_obj
            end+case @typ when ''TD'' then '' on database'' else '''' end
        end, -- pk
    if_exists=
        ''if exists(''+crlf+
        case
        when @typ in (''IX'',''UQ'')
        then
            ''    select top 1 null from sys.indexes''+crlf+
            ''    where name=''''''+@obj+''''''''+crlf+
            ''    and object_id=object_id(''''''+
                 q_Schema+''.''+q_parent+
                 '''''')''+crlf
        when @typ in (''TD'')
        then
            ''    select top 1 null from sys.triggers''+crlf+
            ''    where name=''''''+@obj+''''''''+crlf
        else
            ''    select top 1 null from sys.objects''+crlf+
            ''    where name=''''''+@obj+''''''''+crlf+
            case relaxed
            when 1 then '''' else ''    and [type]=''''''+@typ+''''''''
            end+crlf+
            ''    and schema_id=schema_id(''''''+def_schema+'''''')''+crlf
        end+
        ''    )''

from fn__sym(),(
    select
        quotename(@schema) as q_schema,
        isnull(@schema,''dbo'')as def_schema,
        quotename(@parent) as q_parent,
        quotename(@obj) as q_obj,
        charindex(''|relaxed|'',''|''+isnull(@opt,'''')+''|'') as relaxed
    ) as params
-- fn__script_drop'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_drop:


-- ============================================================== fn__sysobjects
select @ver=dbo.fn__script_sign('fn__sysobjects',1)
if not @ver is null
and @ver=131029.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'fn__sysobjects') with nowait
    goto skip_fn__sysobjects
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sysobjects') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sysobjects'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sysobjects]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131029\s.zaglio:moved from utility_old back to utility
    v:131001.1000\s.zaglio:refined indexes (pk,uq)
    r:130908\s.zaglio:adapted to fn__script_drop
    v:121202\s.zaglio:added parent_id,schema_id,drop_script,if_exists and tested
    r:121201\s.zaglio:added indexes and removed mssql2k compatibility
    v:121012\s.zaglio:added drop column
    v:110629\s.zaglio:list all sysobjects
    t:select * from fn__sysobjects(default,default,default) where typ in (''td'',''tr'')
    t:select * from fn__sysobjects(default,default,''if_exists'') where typ in (''fn'')
    t:select * from fn__sysobjects(default,default,''if_exists|relaxed'') where typ in (''fn'')
    t:sp__sysobjects_test
*/
CREATE function fn__sysobjects(
    @obj sysname,
    @schema_id int,
    @opt sysname    -- drop_script: fill the drop script info
                    -- if_exists: fill the [exists] column
                    -- relaxed: passed to fn__script_drop
    )
returns @t table(
    id int,
    sch sysname null,
    obj sysname,
    typ varchar(2),                     -- in sys.objects is char(2)
    [drop] sysname null,
    parent sysname null,
    parent_typ varchar(2) null,         -- in sys.objects is char(2)
    drop_script nvarchar(512) null,
    if_exists nvarchar(4000) null
    )
as
begin
declare @drop_script bit,@if_exists bit,@relaxed bit,@crlf nvarchar(4)
select @obj=isnull(@obj,''%''),@crlf=crlf from fn__sym()
if not @opt is null
    begin
    select @opt=''|''+@opt+''|''
    select
        @drop_script=charindex(''|drop_script|'',@opt),
        @if_exists=charindex(''|if_exists|'',@opt),
        @relaxed=charindex(''|relaxed|'',@opt)
    end
else
    select
        @drop_script=0,@if_exists=0
/*
AF = funzione di aggregazione (CLR)
C = vincolo CHECK
D = DEFAULT (vincolo o valore autonomo)
F = vincolo FOREIGN KEY
FN = funzione scalare SQL
FS = Funzione scalare di assembly (CLR)
FT = funzione valutata a livello di tabella assembly (CLR)
IF = funzione SQL inline valutata a livello di tabella
IT = tabella interna
P = Stored procedure SQL
PC = Stored procedure di assembly (CLR)
PG = Guida di piano
PK = vincolo PRIMARY KEY
R = regola (tipo obsoleto, autonoma)
RF = procedura-filtro-replica
S = tabella di base di sistema
SN = sinonimo
SQ = coda di servizio
TA = trigger DML assembly (CLR)
TF = funzione valutata a livello di tabella SQL
TR = trigger DML SQL
TT = tipo tabella
U = tabella (definita dall''utente)
UQ = vincolo UNIQUE
V = vista
X = stored procedure estesa
*/
insert @t(id,sch,obj,typ,[drop],parent,parent_typ)
-- declare @schema_id int,@obj sysname select @obj=''%''
select
    o.object_id,s.name,o.[name],o.[type],
    case
    when o.[type] = ''U'' then ''table''
    when o.[type] = ''SN'' then ''synonym''
    when o.[type] = ''P'' then ''proc''
    when o.[type] = ''V'' then ''view''
    when o.[type] = ''TR'' then ''trigger''
    when o.[type] in (''FN'',''FS'',''FT'',''IF'',''TF'') then ''function''
    when o.[type] = ''D'' then ''constraint ''
    when o.[type] in (''PK'',''UQ'',''F'') then ''index ''
    else ''#!unktype ''
    end,
    p.name,p.[type]
-- select top 10 *
from sys.objects o
left join sys.objects p on o.parent_object_id=p.object_id
join sys.schemas s on o.schema_id=s.schema_id
where not o.type in (''s''/*system*/,''tr''/*trigger are in sys.triggers*/)
and o.name like @obj
and (@schema_id is null or @schema_id=o.[schema_id])

union                                   -- db trigger

select
    t.object_id,null,t.name,
    case t.parent_id when 0 then ''TD'' else ''TR'' end,
    ''trigger'',p.name,p.[type]
-- select *
from sys.triggers t
left join sys.objects p
on t.parent_id=p.object_id
where t.is_ms_shipped=0
and t.name like @obj

union

select
    index_id,null,i.name,
    case when is_primary_key=1 then ''PK''
         when is_unique=1 then ''UQ''
         else ''IX''
         end,
    ''index'',p.name,p.[type]
-- select top 10 *
from sys.indexes i
join sys.objects p on i.object_id=p.object_id
join sys.schemas s on p.schema_id=s.schema_id
where 0 in (i.is_primary_key,i.is_unique)
and (p.type!=''s''/* or @sys=1*/)
and i.name like @obj -- heep excluded because null name

/*
if @if_exists=1
    update @t set [exists]=''if exists(''+@crlf
                          +''    select top 1 null from ''+location
                          +''    where name=''''''+obj+''''''''
                          +case
*/

if @drop_script=1 or @if_exists=1
    update @t set
        drop_Script=case @drop_script when 0 then null else f.drop_script end,
        if_exists=case @if_exists when 0 then '''' else f.if_exists end
    from @t t
    cross apply fn__script_drop(t.obj,typ,sch,parent,case @relaxed
                                                     when 1
                                                     then ''relaxed''
                                                     else null
                                                     end) f

return
end -- fn__sysobjects'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sysobjects:


-- ========================================================== fn__script_sysobjs
select @ver=dbo.fn__script_sign('fn__script_sysobjs',1)
if not @ver is null
and @ver=131006.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'fn__script_sysobjs') with nowait
    goto skip_fn__script_sysobjs
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_sysobjs') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_sysobjs'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_sysobjs]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:131006\s.zaglio: adapted to change of flags.svr->flags.srv
    v:130926.1000,130217\s.zaglio:script_%;added enums(517)
    v:121031\s.zaglio:added script_act
    v:120517\s.zaglio:removed from core group
    v:120516.1900\s.zaglio: merged fn__script_events
    d:120516\s.zaglio: fn__script_events
    v:120504\s.zaglio: last change
    v:110620\s.zaglio: added typ field
    v:110527\s.zaglio: added new objs
    v:110510\s.zaglio: lists system objects
    t:select * from fn__script_sysobjs(default)
    t:select * from fn__script_sysobjs((select obj from tids))
    t:select * from fn__script_sysobjs((select ev from tids))
*/
CREATE function fn__script_sysobjs(@tid tinyint)
returns @objs table (
    tid tinyint,
    id int primary key,
    rid int,
    flags smallint,
    cod sysname
    )
as
begin
-- see entire list in
-- ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.it/udb9/html/fb2a7bd0-2347-488c-bb75-734098050c7c.htm
-- http://msdn.microsoft.com/it-it/library/ms179503%28v=sql.105%29.aspx
declare
    @srv smallint,@db smallint,@tsrv smallint,@tdb smallint,
    @ts smallint,@tx smallint,@tt smallint,@tmask smallint,
    @ev tinyint, @obj tinyint, @grp tinyint

select top 1
    -- events
    @srv=srv|ddl,
    @db=db|ddl,
    @tsrv=[type]|srv|ddl,
    @tdb=[type]|db,
    -- sys.objects
    @ts=[sys],
    @tx=[exclude],
    @tt=[tmp]
from flags

select top 1
    @ev=ev,
    @obj=obj,
    @grp=grp
from tids

if @tid is null
insert @objs(   tid,    id,     rid,    flags,  cod)
        select  @grp,   500,     null,   @tmask, ''sys.objects''

if @tid is null or @tid=@obj
insert @objs(   tid,    id,     rid,    flags,  cod)
        select  @obj,   501,      500,   @ts,    ''log''
union   select  @obj,   502,      500,   @ts,    ''cfg''
union   select  @obj,   503,      500,   @ts,    ''obj''
union   select  @obj,   504,      500,   @tt,    ''tmp''
union   select  @obj,   505,      500,   @ts,    ''ids''
union   select  @obj,   506,      500,   @ts,    ''lng''
union   select  @obj,   507,      500,   @tx,    ''dtproperties''
union   select  @obj,   508,      500,   @tt,    ''tst''
union   select  @obj,   509,      500,   @tt,    ''bak''
union   select  @obj,   510,      500,   @ts,    ''flags''
union   select  @obj,   511,      500,   @ts,    ''tids''
union   select  @obj,   512,      500,   @ts,    ''log_ddl''
union   select  @obj,   513,      500,   @ts,    ''tid''
union   select  @obj,   514,      500,   @ts,    ''flg''
union   select  @obj,   515,      500,   @ts,    ''iof''
union   select  @obj,   516,      500,   @ts,    ''script_%''
union   select  @obj,   517,      500,   @ts,    ''enums''
union   select  @obj,   518,      500,   @ts,    ''act''

-- events
if @tid is null or @tid=@ev
insert @objs(   tid,    id,     rid,    flags,  cod)
        select  @ev,    010,      null,   @tsrv,  ''DDL_SERVER_LEVEL_EVENTS''
union   select  @ev,    011,      010,    @srv,   ''create_database''
union   select  @ev,    012,      010,    @srv,   ''alter_database''
union   select  @ev,    013,      010,    @srv,   ''drop_database''
union   select  @ev,    070,      010,    @tdb,   ''DDL_DATABASE_LEVEL_EVENTS''

union   select  @ev,    080,      070,    @tdb,   ''DDL_TABLE_VIEW_EVENTS''

union   select  @ev,    090,      080,    @tdb,   ''DDL_TABLE_EVENTS''
union   select  @ev,    091,      090,    @db,    ''create_table''
union   select  @ev,    092,      090,    @db,    ''alter_table''
union   select  @ev,    093,      090,    @db,    ''drop_table''

union   select  @ev,    100,      080,    @tdb,   ''DDL_TABLE_EVENTS''
union   select  @ev,    101,      100,    @db,    ''create_view''
union   select  @ev,    102,      100,    @db,    ''alter_view''
union   select  @ev,    103,      100,    @db,    ''drop_view''

union   select  @ev,    110,      080,    @tdb,   ''DDL_INDEX_EVENTS''
union   select  @ev,    111,      110,    @db,    ''create_index''
union   select  @ev,    112,      110,    @db,    ''alter_index''
union   select  @ev,    113,      110,    @db,    ''drop_index''
union   select  @ev,    114,      110,    @db,    ''create_xml_index''

union   select  @ev,    130,      070,    @tdb,   ''DDL_SYNONYM_EVENTS''
union   select  @ev,    131,      130,    @db,    ''create_synonym''
union   select  @ev,    132,      130,    @db,    ''drop_synonym''

union   select  @ev,    140,      070,    @tdb,   ''DDL_FUNCTION_EVENTS''
union   select  @ev,    141,      140,    @db,    ''create_function''
union   select  @ev,    142,      140,    @db,    ''alter_function''
union   select  @ev,    143,      140,    @db,    ''drop_function''

union   select  @ev,    150,      070,    @tdb,   ''DDL_PROCEDURE_EVENTS''
union   select  @ev,    151,      150,    @db,    ''create_procedure''
union   select  @ev,    152,      150,    @db,    ''alter_procedure''
union   select  @ev,    153,      150,    @db,    ''drop_procedure''

union   select  @ev,    160,      070,    @tdb,   ''DDL_TRIGGER_EVENTS''
union   select  @ev,    161,      160,    @db,    ''create_trigger''
union   select  @ev,    162,      160,    @db,    ''alter_trigger''
union   select  @ev,    163,      160,    @db,    ''drop_trigger''

union   select  @ev,    170,      070,    @tdb,   ''DDL_TYPE_EVENTS''
union   select  @ev,    171,      170,    @db,    ''create_type''
union   select  @ev,    173,      170,    @db,    ''drop_type''

union   select  @ev,    200,      070,    @tdb,   ''DDL_DATABASE_SECURITY_EVENTS''

union   select  @ev,    220,      200,    @tdb,   ''DDL_USER_EVENTS''
union   select  @ev,    221,      220,    @db,    ''create_user''
union   select  @ev,    222,      220,    @db,    ''alter_user''
union   select  @ev,    223,      220,    @db,    ''drop_user''

union   select  @ev,    230,      200,    @tdb,   ''DDL_ROLE_EVENTS''
union   select  @ev,    231,      230,    @db,    ''create_role''
union   select  @ev,    232,      230,    @db,    ''alter_role''
union   select  @ev,    233,      230,    @db,    ''drop_role''

union   select  @ev,    250,      200,    @tdb,   ''DDL_SCHEMA_EVENTS''
union   select  @ev,    251,      250,    @db,    ''create_schema''
union   select  @ev,    252,      250,    @db,    ''alter_schema''
union   select  @ev,    253,      250,    @db,    ''drop_schema''

union   select  @ev,    260,      200,    @tdb,   ''DDL_GDR_DATABASE_EVENTS''
union   select  @ev,    261,      260,    @db,    ''grant_database''
union   select  @ev,    262,      260,    @db,    ''deny_database''
union   select  @ev,    263,      260,    @db,    ''revoke_database''

-- special data events
union   select  @ev,    270,      null,   @tdb,   ''DML_EVENTS''
union   select  @ev,    271,      270,    @tdb,   ''insert_data''

-- special control events
union   select  @ev,    280,      null,   @tdb,   ''CTRL_EVENTS''
union   select  @ev,    281,      280,    @tdb,   ''group_objects''

return
end -- fn__script_sysobjs'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_sysobjs:


-- ============================================================ fn__str_distance
select @ver=dbo.fn__script_sign('fn__str_distance',1)
if not @ver is null
and @ver=121007.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'fn__str_distance') with nowait
    goto skip_fn__str_distance
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_distance') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_distance'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_distance]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:121007\s.zaglio: a remake (see inside code comments)
    v:081130\S.Zaglio: Levenshtein algo. Originally from http://www.marcopipino.it/sql/levenshtein.php
    t:
        print dbo.fn__str_distance(''stefano'',''stefania'',default) -- 1.5
        print dbo.fn__str_distance(''stefano'',''giovanni'',default) -- 7.5
        print dbo.fn__str_distance(''dal-7134ab1'',''dal=7134ab1'',default)     -- 1
        print dbo.fn__str_distance(''dal-7134ab1'',''dal7134ab1'',default)      -- 1.5
        print dbo.fn__str_distance(''dal-7134ab1'',''dal7134ab11'',default)     -- 2
        print dbo.fn__str_distance(''C.SO UMBERTO I'',''PIAZZA UMBERTO I'',10)  -- 6
        print dbo.fn__str_distance(''C.SO UMBERTO I'',''PIAZZA UMBERTO I'',40)  -- 5
*/
CREATE function dbo.fn__str_distance(
    @s1 nvarchar(255),
    @s2 nvarchar(255),
    @maxoffset int      -- default is 5
)
returns float
as
begin
/* this is the choose after some test between 5 fn over 9900 streets:
1. the original from http://www.marcopipino.it/sql/levenshtein.php
   4m 45s 476ms
2. LEVENSHTEIN(@s,@t)
   41s 893ms
3. edit_distance_within
   8s 70ms
4. Sift3distance2
   956ms
5. udfDamerauLevenshteinLim
   10s 166ms
Maybe the 5 can be better but I have no time for a deep test
The Sift3distance2 is more accurate in some cases:
    VIA DEI FRENTANI    VIA DEI VESTINI     7,5 vs 4 in all other fn
and less accurate in some other cases:
    C.SO UMBERTO I        PIAZZA UMBERTO I    15 vs 6 at offset 35
*/

declare @s1len int,@s2len int

select @s1len=len(isnull(@s1,'''')),@s2len=len(isnull(@s2,''''))

if @s1len=0 return @s2len
else
if @s2len=0 return @s1len

if isnull(@maxoffset,0)=0 set @maxoffset=5

declare
    @currpos int,@matchcnt int,@wrkpos int,
    @s1offset int,@s1char varchar,@s1pos int,@s1dist int,
    @s2offset int,@s2char varchar,@s2pos int,@s2dist int

select @s1offset=0,@s2offset=0,@matchcnt=0,@currpos=0

while(@currpos+@s1offset<@s1len and @currpos+@s2offset<@s2len)
begin
    set @wrkpos=@currpos+1

    if(substring(@s1,@wrkpos+@s1offset,1)=substring(@s2,@wrkpos+@s2offset,1))
        set @matchcnt=@matchcnt+1
    else
    begin
        set @s1offset=0
        set @s2offset=0
        select @s1char=substring(@s1,@wrkpos,1),
               @s2char=substring(@s2,@wrkpos,1)
        select @s1pos=charindex(@s2char,@s1,@wrkpos)-1,
               @s2pos=charindex(@s1char,@s2,@wrkpos)-1
        select @s1dist=@s1pos-@currpos,@s2dist=@s2pos-@currpos
        if(@s1pos>0 and (@s1dist<=@s2dist or @s2pos<1) and @s1dist<@maxoffset)
            set @s1offset=(@s1pos-@wrkpos)+1
        else
        if(@s2pos>0 and (@s2dist<@s1dist or @s1pos<1) and @s2dist<@maxoffset)
            set @s2offset=(@s2pos-@wrkpos)+1
    end

    set @currpos=@currpos+1
end

return (@s1len+@s2len)/2.0-@matchcnt
end -- fn__str_distance'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_distance:


-- =============================================================== sp__deprecate
select @ver=dbo.fn__script_sign('sp__deprecate',1)
if not @ver is null
and @ver=130903.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'sp__deprecate') with nowait
    goto skip_sp__deprecate
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__deprecate') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__deprecate'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__deprecate]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    todo:manage version
    v:130903\s.zaglio:renamed from sp__deprecated
    v:121004.1614\s.zaglio: used to delete D tagged objects objects
    t:sp__deprecated ''test_scripting''
*/
CREATE proc sp__deprecate
    @obj sysname = null,
    @ver numeric(10,4) = null
as
begin
set nocount on
declare @proc sysname, @err int, @ret int, @type sysname,@sql nvarchar(4000)
select @proc=object_name(@@procid),@err=0,@ret=0
if isnull(@obj,'''')='''' goto help

select @type=[drop]
from fn__sysobjects(@obj,default,default)
if @type is null return @ret
-- select distinct [type] from sys.objects
select @sql=''drop ''+@type+'' ''+quotename(@obj)
if not @sql is null
    begin
    raiserror(''drop deprecated "%s"(%s)'',10,1,@obj,@type) with nowait
    exec(@sql)
    end

return @ret
help:
exec sp__usage @proc,''
Scope
    drop deprecated object is exists
    (fast and smaller version of sp__drop)
''
return -1
end -- proc sp__deprecated'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__deprecate:


-- =========================================================== sp__script_update
select @ver=dbo.fn__script_sign('sp__script_update',1)
if not @ver is null
and @ver=140204.0000
    begin
    raiserror('skipped "%s.%s" because is the same',
              10,1,@db,'sp__script_update') with nowait
    goto skip_sp__script_update
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_update') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_update'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_update]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:test,conflict,difference,upgrade
    v:140204\s.zaglio:better error management
    v:140116.1100\s.zaglio:added better compare output, help and use of context info
    v:140108.1518\s.zaglio:set of default @tcc to 10 and added ovr option
    v:131216.1500\s.zaglio:added STRICT option and correct a bug near compare
    v:131215\s.zaglio:refactor and added relaxed check of ver without .hhmm
    v:131210.1000\s.zaglio:compare for @tcc common comments and overwrite dst
    v:131103\s.zaglio:a bug when pair conflict\common (conflict===update)
    v:131018.1210\s.zaglio:added SD option and removed initial blnk lines
    v:130908.0100\s.zaglio:converted to update utility
    r:121006\s.zaglio:replacing script header template
    t:sp__script_update_test @dbg=2
*/
CREATE proc sp__script_update
    @src nvarchar(max) = null,
    @db  sysname = null,
    @tcc int = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000),@err_sev int-- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end

-- ============================================================== declaration ==
declare
    -- object and its type
    @obj sysname,@t_type nvarchar(4),
    @sch sysname,@params sysname,           -- used by sp_executesql
    -- new object info
    @pcmd int,                              -- create/alter position
    @cmd sysname,                           -- create/alter
    @otype sysname,                         -- proc/func/...
    @t_obj_id int,                          -- target object id
    -- local vars
    @tag char,
    @s_obj sysname,                         -- sub object commodity
    @ver sysname,                           -- version commodity
    @sql nvarchar(max),                     -- sql commodity
    @dst nvarchar(max),                     -- destination/target src
    @drop nvarchar(4000),
    @crlf nvarchar(2),
    @sp__update_log sysname,
    @sp__update_log_id int,
    @threshold_last_cmt tinyint,            -- see below
    @cmts_min_distance tinyint,             -- see below
    @n int,@m int,
    -- options
    @test bit,@sd bit,@strict bit,@ovr bit,
    -- status
    @sts char,
    @st_common tinyint,
    @st_conflict tinyint,
    @st_update tinyint,                       -- target(correct) update
    @st_left_update tinyint,                  -- new il order
    @st_err tinyint,
    @st_unk tinyint,

    @end_declare bit

declare @nfo table (
    tag varchar(4),row smallint,
    val1 sysname,val2 sysname,val3 sysname
    )

declare @cmprs table(
    row int,
    b_ver numeric(10,4),
    m_ver numeric(10,4),
    diff float,
    t_row smallint,n_row smallint,
    t_ver numeric(10,4) null,n_ver numeric(10,4) null,
    t_aut sysname null,n_aut sysname null,
    t_cmt nvarchar(512) null,n_cmt nvarchar(512) null,
    sts tinyint null
    )

-- target or current object info
declare @dst_nfo table(
    row smallint not null,
    ver numeric(10,4) null,
    aut nvarchar(4000) null,
    cmt nvarchar(4000) null,
    hhmm bit not null,
    tick smallint,                  -- sub sequence when doubled version
    lrow smallint
    )

-- source or new object info
declare @src_nfo table(
    row smallint not null,
    ver numeric(10,4) null,
    aut nvarchar(4000) null,
    cmt nvarchar(4000) null,
    hhmm bit not null,
    tick smallint,                  -- sub sequence when doubled version
    diff float,                     -- distance between dst.val3 and src.val3
    lrow smallint
    )

-- =========================================================== initialization ==

if @db is null select @db=db_name()
select
    @tcc=isnull(@tcc,10),           -- this values is from experience and must
                                    -- be modified only if very sure
    @threshold_last_cmt = 3,        -- same above consideration
    @cmts_min_distance  = 4,        -- same of above
    @crlf=crlf,
    @test=charindex(''|test|'',@opt),
    @sd=charindex(''|sd|'',@opt),
    @strict=charindex(''|strict|'',@opt),
    @ovr=charindex(''|ovr|'',@opt)|dbo.fn__context_info(@proc+'':ovr''),
    @st_unk=0,
    @st_common=1,
    @st_update=2,
    @st_conflict=3,
    @st_left_update=4,
    @st_err=5
from fn__sym()

-- ======================================================== second params chk ==

if nullif(@src,'''') is null goto help

-- =============================================================== #tbls init ==

select @sp__update_log_id=isnull(object_id(''tempdb..#sp_update_log''),0)
if @sp__update_log_id=0 -- if not defined outside
    begin
    exec(''create proc #sp__update_log
                        @obj sysname,
                        @st char,
                        @msg nvarchar(2000) = null
          as
          declare @sev int
          select @sev=10
          if not object_id(''''tempdb..#update_log'''') is null
            insert #update_log(obj,msg,sts)
            select @obj,@msg,@st
          -- Updated,New,Same,Older,Conflict,Error
          select @msg=case @st when ''''E'''' then @msg
                               when ''''U'''' then ''''updated-re-created''''
                               when ''''N'''' then ''''new-created''''
                               when ''''S'''' then ''''same-not replaced''''
                               when ''''O'''' then ''''older-not replaced''''
                               when ''''C'''' then ''''conflict''''+
                                               isnull('''':''''+@msg,'''''''')
                               end
          if @st=''''C'''' select @sev=11
          if @st=''''E'''' select @sev=16
          select @msg=@obj+'''':''''+@msg
          raiserror(@msg,@sev,1)
         '')
    end

select @sp__update_log=''#sp__update_log''

-- ===================================================================== body ==
/* get object name
    proc
    func
    view
    tabl
    syno
*/

if @dbg>0 exec sp__printf ''-- %s: debugging level:%d'',@proc,@dbg

-- ================================================= get info from new script ==

-- remove initial blank lines
while left(@src,1) like ''[%''+@crlf+''%]'' select @src=stuff(@src,1,1,'''')

insert @nfo
select tag,row,val1,val2,val3
from fn__script_info_tags(@src,''ad#'',default)

if @dbg>1 select ''new'' [?],* from @nfo

select @obj=parsename(val3,1),@pcmd=row,@cmd=val1,@otype=val2
from @nfo where tag=''#''

if @cmd!=''create'' raiserror(''command "%s" not managed'',16,1,@cmd)
if not @otype in (''proc'',''procedure'',''func'',''function'',''view'',''trigger'')
    begin
    select @err_msg=''object type "''+@otype+''" not managed''
    exec @sp__update_log @obj,''E'',@err_msg
    end

-- ============================================== get info from target object ==
-- target object cannot be locked, so do not use (nolock)
select @dst=''
use [%db%]
select @id=o.object_id, @type=o.[type],@code=m.definition,@sch=sch
from (
    select object_id,name,type,object_schema_name(object_id) sch
    from sys.objects
    where name="%obj%"
    union
    select object_id,name,type,object_schema_name(object_id) sch
    from sys.triggers
    where name="%obj%"
    ) o
join sys.all_sql_modules m on o.object_id=m.object_id
''

exec sp__str_replace @dst out,''"|%db%|%obj%'','''''''',@db,@obj

select @params=N''@id int out,@type nvarchar(4) out,@code nvarchar(max) out,''
              +N''@sch sysname out''

exec sp_executesql
        @dst,
        @params,
        @id=@t_obj_id out,@type=@t_type out,@code=@dst out,@sch=@sch out

if @t_obj_id is null
    begin
    select @sts=''N'' -- new
    goto do_action
    end

-- ================================================================== compare ==

if @ovr=1
    begin
    exec sp__printf ''-- OVERRIDE option enabled''
    select @sts=''U''
    goto do_action
    end

begin try

    insert into @dst_nfo(ver,aut,cmt,row,hhmm,tick,lrow)
    select
        cast(ver as numeric(10,4)),aut,cmt,
        row,hhmm,
        row_number() over (partition by ver,aut order by ver,row desc)-1 as tick,
        row_number() over (order by row)-1 as lrow
    from (
        -- convert aaaammgg.hhmm to aammgg.hhmm
        select case
               when val1<999999
               then val1
               else val1-cast(val1/1000000 as int)*1000000
               end as ver,val2 as aut,val3 as cmt,row,hhmm
        from (
            select
                cast(val1 as numeric(12,4)) as val1,val2,val3,
                row,charindex(''.'',val1) as hhmm
            from fn__script_info_tags(@dst,''rv'',default)
            where isnumeric(val1)=1 -- skip non coerent comments
            ) nfo
        ) nfo
    order by row
    select @m=@@rowcount

    insert into @src_nfo(ver,aut,cmt,row,hhmm,tick,lrow)
    select
        cast(ver as numeric(10,4)),aut,cmt,
        row,hhmm,
        row_number() over (partition by ver,aut order by ver,row desc)-1 as tick,
        row_number() over (order by row)-1 as lrow
    from (
        -- convert aaammgg.hhmm to aammgg.hhmm
        select case
               when val1<999999
               then val1
               else val1-cast(val1/1000000 as int)*1000000
               end as ver,val2 as aut,val3 as cmt,row,hhmm
        from (
            select
                cast(val1 as numeric(12,4)) as val1,val2,val3,
                row,charindex(''.'',val1) as hhmm
            from fn__script_info_tags(@src,''rv'',default)
            where isnumeric(val1)=1 -- skip non coerent comments
            ) nfo
        ) nfo
    order by row
    select @n=@@rowcount

    if @n=0 exec @sp__update_log @obj,''E'',''cannot identify source properties''
    if @m<2 -- if destination has not comments
        begin
        select @sts=''U''
        goto do_action
        end

    -- some automatic adjustements to prevent user''s fortuity
    if @strict=0
        begin
        -- correct dates with .0000 binding to comment
        update @src_nfo set ver=ver+tick/10000.0 where hhmm=0
        update @dst_nfo set ver=ver+tick/10000.0 where hhmm=0

        -- adjust similar comments
        update src set diff=dbo.fn__str_distance(src.cmt,dst.cmt,default)
        from @src_nfo src
        join @dst_nfo dst on src.ver=dst.ver and src.aut=dst.aut

        if @dbg>2
            begin
            select ''@src_info'' tbl,* from @src_nfo order by row
            select ''@dst_info'' tbl,* from @dst_nfo order by row
            end

        end -- relaxed or strict=0

    ;with
        compare as
        (
        select isnull(t_ver,n_ver) m_ver,*
        from (
            select
                t.row as t_row,
                n.row as n_row,
                t.ver as t_ver,
                t.aut as t_aut,
                t.cmt as t_cmt,
                n.ver as n_ver,
                n.aut as n_aut,
                n.cmt as n_cmt,
                case when
                    n.lrow=0 and t.lrow=0               -- only last row
                    and n.ver-t.ver between 0 and 1     -- same date
                    and n.aut=t.aut                     -- same author
                    and dbo.fn__str_distance(n.cmt,t.cmt,default)
                        <
                        len(n.cmt)/@cmts_min_distance
                then 0
                else
                    dbo.fn__str_distance(n.cmt,t.cmt,default)
                end as diff
            from @dst_nfo as t                      -- target/current version
            full join @src_nfo as n                 -- source/new version
            on t.ver=n.ver and t.aut=n.aut          -- aligned by datetime and author
            or (-- or is a last ...
                n.lrow=0 and t.lrow=0
                -- ... daily change ...
                and n.ver-t.ver between 0 and 1
                -- ... by same user ...
                and n.aut=t.aut
                -- ... with a small change
                and dbo.fn__str_distance(n.cmt,t.cmt,default)
                    <
                    len(n.cmt)/@cmts_min_distance
                )
            ) a
        ),

        -- find 1st common base ver
        base_ver(b_ver) as
        (
        -- select the older
        select top 1 b_ver
        from (
            -- take last 4 common comments
            select top (@tcc) t_ver as b_ver
            from compare
            where t_ver=n_ver
            order by m_ver desc
            ) commons
        order by b_Ver
        )

    insert into @cmprs(
        row,b_ver,m_ver,diff,t_row,n_row,t_ver,n_ver,t_aut,n_aut,t_cmt,n_cmt,sts
        )
    select
        row_number() over(order by m_ver desc),
        (select top 1 b_ver from base_ver) as b_ver,
        m_ver,diff,t_row,n_row,t_ver,n_ver,t_aut,n_aut,t_cmt,n_cmt,
        case
        -- do not change order of whens
        when (not n_cmt is null and t_cmt is null)
          or (diff=0 and t_cmt!=n_cmt) then @st_update
        when (n_cmt is null and not t_cmt is null) then @st_left_update
        when (diff<=@cmts_min_distance) then @st_common
        when (diff>@cmts_min_distance) then @st_conflict
        else @st_err
        end sts
    from compare
    where m_ver>=(select top 1 b_ver from base_ver)
    order by m_ver desc -- merged version

end try
begin catch
    -- possible fn__script_info_tags error
    if @dbg>1 exec sp__printsql @dst
    select @err_msg=left(error_procedure(),len(@sp__update_log))
    if @err_msg!=@sp__update_log
        select @err_msg=''SP inside error at line ''+cast(error_line() as sysname)
                       +'' maybe near fn__script_info_tags; check header with @dbg=2''
    else
        select @err_msg=error_message()
    select @err_sev=error_severity()
    raiserror(@err_msg,@err_sev,1)
end catch


-- complete unknown situations
update @cmprs set sts=@st_unk where sts is null

-- =================================================================== checks ==

if @dbg>1
    begin
    select
        @db as db,''@cmprs'' tbl,*,
        case sts
        when @st_common then ''common''
        when @st_update then ''update''
        when @st_conflict then ''conflict''
        when @st_err then ''algorithm missing error''
        when @st_unk then ''algorithm unknown error''
        when @st_left_update then ''left_update''
        end as [status]
    from @cmprs
    end

if exists(select top 1 null from @cmprs where sts in (@st_err,@st_unk))
    exec @sp__update_log @obj,''E'',''algorithm''

-- if no common base comment
if not exists(select top 1 null from @cmprs where sts=@st_common)
and (select count(*) from @dst_nfo)>0   -- 131210\s.zaglio
    exec @sp__update_log @obj,''E'',''too much different releases''
-- overwrite targets that do not has comments (without header)

select @sts=''''

declare @mark_a int,@mark_b int
select @mark_a=null,@mark_b=null
select top 1
    @mark_a=case
            when a.sts=@st_conflict
            then a.n_row
            else b.n_row
            end
from @cmprs a
left join @cmprs b
on b.row=a.row+1
where 1=0
   -- (a.sts=@st_common and isnull(b.sts,@st_common)=@st_update)
   or (a.sts=@st_common and isnull(b.sts,@st_common)=@st_left_update)
   or (a.sts=@st_update and isnull(b.sts,@st_common)=@st_left_update)
   or (a.sts=@st_conflict)
order by a.row
if @@rowcount>0 select @sts=''C'',@mark_b=@mark_a

-- if same or small comment difference
if @sts=''''
and not exists(select top 1 null from @cmprs where sts!=@st_common)
    begin
    if exists(select top 1 null
              from @cmprs
              where t_cmt!=n_cmt)
        select @sts=''U''
    else
        select @sts=''S''
    end

-- if newer
if @sts=''''
and exists (
    select top 1 null
    from @cmprs c
    where c.row=1 and c.sts=@st_update and t_cmt is null
    )
    select @sts=''U''

-- if newer (for last,quick,small modification)
if @sts=''''
and not exists (
    select top 1 null
    from @cmprs c
    -- where c.row=1 and c.sts=@st_update and not t_cmt is null and n_ver>=t_ver
    where not c.sts in (@st_update,@st_common)
    )
    select @sts=''U''

-- older
if @sts='''' select @sts=''O''

-- if @sts='''' raiserror(''inside error, unmanaged condition'',16,1)

-- ============================================ show result status of compare ==
do_action:

if @dbg>1 select @sts as ''------------------- result_action -------------------''

-- check for aliases and deprecates
declare cs cursor local for
    select tag,val1 as ver,val3 as obj
    from @nfo
    where @sts!=''C''    -- <<<<<<<<<<<<< NB >>>>>>>>>>>
      and tag in (''a'',''d'')
    order by val1
open cs
while 1=1
    begin
    fetch next from cs into @tag,@ver,@s_obj
    if @@fetch_status!=0 break
    select @sql=''use ''+quotename(@db)+@crlf
    if @tag=''D'' select @sql=@sql+''exec sp__deprecate ''''''+@s_obj+'''''',''+@ver
    if @tag=''A'' select @sql=@sql+''exec sp__aliases ''''''+@s_obj+'''''',@of=''''''+@obj+''''''''
    begin try
        if @test=0 exec(@sql)
    end try
    begin catch
        select @err_msg=error_message()
        exec @sp__update_log @obj,''E'',@err_msg
    end catch
    end -- cursor cs
close cs
deallocate cs

if @sts in (''O'',''S'')    -- older,same
    begin
    exec @sp__update_log @obj,@sts
    goto dispose
    end

if @sts=''C''             -- conflict
    begin
    if @sd=1
        if @test=1                      -- show difference of lines
            select
                ''diff'' tbl,isnull(a.lno,b.lno) lno,@db as db,@obj as obj,
                isnull(b.line,'''') as [current(A)],
                isnull(a.line,'''') as [new(B)]
            from fn__ntext_to_lines(@src,0) a
            full outer join fn__ntext_to_lines(@dst,0) b
            on a.line=b.line and abs(a.lno-b.lno)<10
            order by isnull(a.lno,b.lno)
        else                            -- show the two sequence of lines
            begin
            select @sql=''
            select
                isnull(b.line,'''''''') as [''+@db+''.''+@obj+''],
                isnull(a.line,'''''''') as [new code],
                case
                when a.lno between @mark_a and @mark_b then ''''<<''''
                else ''''''''
                end [??]
            from fn__ntext_to_lines(@src,0) a
            full outer join fn__ntext_to_lines(@dst,0) b
            on a.lno=b.lno
            order by isnull(a.lno,b.lno)''
            exec sp_executesql
                    @sql,
                    N''@src nvarchar(max),@dst nvarchar(max),@mark_a int,@mark_b int'',
                    @src=@src,@dst=@dst,@mark_a=@mark_a,@mark_b=@mark_b
            end
        -- sd option

    exec @sp__update_log @obj,@sts -- raiserror 11
    end -- conflict

-- ================================================================ do update ==

if @sts!=''N''
    select
        @drop=if_exists+@crlf+''    ''+drop_script
    from fn__script_drop(@obj,@t_type,@sch,default,''relaxed'')

-- remove initial crlf
while left(@src,len(@crlf))=@crlf select @src=stuff(@src,1,len(@crlf),'''')

select @src=''use ''+quotename(@db)+@crlf
           +''begin try''+@crlf
           +''begin tran sp__script_update''+@crlf
           +isnull(@drop+@crlf,'''')
           +''exec(''''''+replace(@src,'''''''','''''''''''')+'''''')''+@crlf
           +''commit tran sp__script_update''+@crlf
           +''end try''+@crlf
           +''begin catch''+@crlf
           +''rollback tran sp__script_update''+@crlf
           +''declare @m nvarchar(2000),@s int''+@crlf
           +''select @m=error_message(),@s=error_severity()''+@crlf
           +''raiserror(@m,@s,1)''+@crlf
           +''end catch''

begin try
    -- todo: encose in trans and manage nested
    if @dbg>0 exec sp__printsql @src
    if @test=0 exec(@src)
-- ================================================================ run SETUP ==
    if @obj like ''%[_]setup''
        begin
        if @dbg>0 exec sp__printf ''-- will be executed as setup''
        else
            begin
            if @test=0 exec (''exec ''+@obj+'',@opt=''''run'''''')
            end
        end
    exec @sp__update_log @obj,@sts
end try
begin catch
    select @err_msg=error_message(),@err_sev=error_severity()
    exec @sp__update_log @obj,''E'',@err_msg
end catch

-- ================================================================== dispose ==

dispose:
if @sp__update_log_id=0 drop proc #sp__update_log -- if it come from outside

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    analyze the script and update of relative object in the specified db

Notes
    - be careful with grouped comments (left the lasts as single)
    - actually drop and re-create the object because too difficult understand
      when sub-type deeply change (example TF to IF)
    - for a complete list of test cases see sp__script_update_test
    - tag D is performed (sp__deprecate @db.@obj,@ver,@aut )
    - if the name of object end with "_setup", the sp will be executed with
      @opt=run (be sure that the sp of setup must run more times)
    - commonly this util will be used by your deploy engine as:
        - create deploy log
        - create #update log
        - call sp for each new object
        - save #update log into deploy log
        - eventually repeat script 3 times (for dependencies)
        - send email with report or other
    - version and comments are used to check effective code progression
        - the possible conditions are:
            - regular (right) upgrade (only new comments on right side)
            - nothing to do (only common comments)
            - conflict (one or more comments on the left)
            - upgrade of merge or minor update (last comment changed on the right)
            - older (less comments in the new script)

        id current                    new                           status
        -- -----------------------  ---------------------------        ----------
        01 yymmd3\auth0:comment3    yymmd3.hhmm\auth0:comment3.x    merged or reviewd
        02       (not present)      yymmd2\auth1:comment2           new line
        03 yymmd1\auth2:comment1       (not present)                conflict or newer
        04 yymmd0\authX:commentXXX  yymmd0\auth3:commentYYY         conflict
        05 yymmd0\auth3:comment0    yymmd0\auth3:comment0           last common

        - lasts 10 comments are taken from each side and compared
        - if "current" has more comments than "new", means that current is newer
        - if 1st comment change a bit in the comment and/or in the time, is a small
          revision

Todo
    - if the new script present a "a"(sp__alias @db.obj,@obj),"j" tag, "t" tag


Parameters
    [param]     [desc]
    #update_log (optional) store update status
                create table #update_log(
                    id int identity,
                    dt datetime default(getdate()) not null,
                    who sysname default system_user+''''@''''+
                                        isnull(host_name(),''''???''''),
                    obj sysname,
                    sts char,       -- Updated,New,Same,Older,Conflict,Error
                    msg nvarchar(2000)
                    )
                anyway a raiserror 11 is given
    @src        source script with "new" version
    @db         target db (default is local),
    @tcc        top common comments to check from history (default:%p1%)
                useful when a bug fix is integrated with same comment in
                a future version where another user had previusly added
                a new feature (see example in notes)
    @opt        options
                test    do not update, test only the possibility (no conflicts)
                sd      show a two column table with two codes, to simplify
                        cut and paste into Kdiff or similar when in conflict
                strict  do not replace missed .hhmm
                ovr     force override (update or create)
                        the local OVR is overridden bye a global OVR, enabled with:
                            sp__context_info ''''sp__script_update:ovr''''
    @dbg        1=base info
                2=compare table
                3=middle and campare table

Examples
    sp__script_update_test 1,@dbg=1
'',@p1=@tcc

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"
if @sp__update_log_id=0 and not object_id(''tempdb..#sp__update_log'') is null
    drop proc #sp__update_log

-- exec @ret=sp__err @cod=@proc,@opt=''ex''
declare @e_msg nvarchar(4000),@e_sev int

-- this come always from #sp
select @e_msg=error_message(),@e_sev=error_severity()
raiserror(@e_msg,@e_sev,1)
return @ret
end catch   -- proc sp__script_update'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_update:


-- ##########################
-- ##
-- ## remove deprecated
-- ##
-- ########################################################
exec sp__deprecate 'sp__release',120823
exec sp__deprecate 'sp__objs',120821
exec sp__deprecate 'fn__crc32_table',120516
exec sp__deprecate 'fn__script_events',120516
exec sp__deprecate 'sp__config',131117
exec sp__deprecate 'fn__crc16_tbl',130202
exec sp__deprecate 'fn__crc32_tbl',130202
exec sp__deprecate 'fn__flds_row',100228
exec sp__deprecate 'fn__parseurl',130906
exec sp__deprecate 'fn__parsefilename',120123
exec sp__deprecate 'fn__object_id',130922
exec sp__deprecate 'sp__clientip',130517
exec sp__deprecate 'sp__serverip',130517
exec sp__deprecate 'sp__parse_conn',130517
exec sp__deprecate 'fn__sql_simplify',120809
exec sp__deprecate 'fn__chk_str',121004
exec sp__deprecate 'fn__taxcode',130830
exec sp__deprecate 'fn__street',140127
exec sp__deprecate 'sp_info',140127
exec sp__deprecate 'sp_find',140103
exec sp__deprecate 'sp__parse',130806
exec sp__deprecate 'sp__parse_test',130806
exec sp__deprecate 'fn__flds_list',121003
exec sp__deprecate 'sp__flds_of',121003
exec sp__deprecate 'sp__search',121003
exec sp__deprecate 'sp__get_tmp',131208
exec sp__deprecate 'sp__maint_stats',130630
exec sp__deprecate 'sp__log_show',100314
exec sp__deprecate 'sp__log_search',100314
exec sp__deprecate 'sp__trace_search',100314
exec sp__deprecate 'sp__perf_top25',110926
exec sp__deprecate 'sp__util_profile',130630
exec sp__deprecate 'sp__util_sqltrace',130630
exec sp__deprecate 'sp__scropt',111027.1000
exec sp__deprecate 'sp__table_align',120727
exec sp__deprecate 'sp__script_copytable',120727
exec sp__deprecate 'sp__assembly',121027
exec sp__deprecate 'sp__script_vtable',120508
exec sp__deprecate 'sp__script_tofile',130824
exec sp__deprecate 'sp__script_info_tags_test_old',130519
exec sp__deprecate 'sp__script_copy',120910
exec sp__deprecate 'sp__util_ddl',120103
exec sp__deprecate 'sp__script_trace',110623
exec sp__deprecate 'sp__select',130521
exec sp__deprecate 'sp__checkout',130902
exec sp__deprecate 'sp__script_deploy',130902
exec sp__deprecate 'sp__script_svn_log',120823
exec sp__deprecate 'sp__web_get',121018

-- ##########################
-- ##
-- ## group objects
-- ##
-- ########################################################
-- =========================================================== sp__utility_setup
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__utility_setup',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140109.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__utility_setup') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__utility_setup') with nowait
        goto skip_sp__utility_setup
        end
    if @ver>140109.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__utility_setup') with nowait
        goto skip_sp__utility_setup
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__utility_setup') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__utility_setup'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__utility_setup]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:140109.1000\s.zaglio:moved call of sp__job_test into sp__utility_setup_test
    v:140103\s.zaglio:add call of sp__job_test
    v:131223.1110\s.zaglio:log_ddl.svr->srv
    v:131212\s.zaglio:removed use of sp__printf
    v:131125\s.zaglio:moved here the update of log_ddl
    v:130922\s.zaglio:about cfg
    v:130830\s.zaglio:managed a problem of fn__find when fnd is wrong
    v:130828\s.zaglio:due difficult to understand clr status I changed strategy
    v:130730.1800\s.zaglio:removed test of 130606 because too generic; added synonym
    v:130606\s.zaglio:added test for use of old sp__printf
    v:130528\s.zaglio:tuned test about CLR
    v:121209\s.zaglio:added check for awe m.m.
    v:121031\s.zaglio:commented clr and modified assembly for 2.0x32
    v:120920\s.zaglio:added opt run
    v:120907\s.zaglio:added script_act
    v:120824\s.zaglio:better 0 condition, removed sp__ dependencies
    d:120823\s.zaglio:sp__release
    d:120821\s.zaglio:sp__objs
    v:120724.1800\s.zaglio: create and upgrade objects
    t:sp__utility_setup ''run''
    t:sp__utility_setup ''run|log_ddl''
*/
CREATE proc [dbo].[sp__utility_setup]
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=isnull(''|''+@opt+''|'','''')

-- ========================================================= param formal chk ==
if charindex(''|run|'',@opt)=0 goto help

-- ============================================================== declaration ==

declare
    @upg_log_ddl bit,
    @begin bit,@end bit,                -- called at begin of script or at end
    @tmp nvarchar(max),
    @sign sysname,
    @log_ddl bit

-- =========================================================== initialization ==

select
    @log_ddl=charindex(''|log_ddl|'',@opt),
    @begin=charindex(''|begin|'',@opt),
    @end=charindex(''|end|'',@opt)

-- ======================================================== second params chk ==
if @log_ddl=1 goto log_ddl

-- ##########################
-- ##
-- ## begin
-- ##
-- ########################################################

-- ===================================================================== body ==

log_ddl:
/*  131125\s.zaglio:moved here the upgrade of log_ddl because executed
                    before a function like fn__script_trace that uses this
                    table and that will fail on compilation
*/
if not object_id(''log_ddl'') is null
    begin
    -- ifolder version of utility or log_ddl
    if object_id(''fn__script_sign'') is null
        select @upg_log_ddl=1
    else
        -- select dbo.fn__script_sign(''log_ddl_bak'',default)
        -- select dbo.fn__script_sign(''log_ddl'',default)
        if dbo.fn__script_sign(''log_ddl'',default)=1612475947
            begin
            raiserror(''-- minor upgrade of log ddl'',10,1)
            exec sp_rename ''log_ddl.svr'' , ''srv'', ''column'' --> 1679584815
            end
        if dbo.fn__script_sign(''log_ddl'',default)=1477379425
            select @upg_log_ddl=1

    if @upg_log_ddl=1
        begin
        raiserror(''-- upgrading log_ddl'',10,1)
        if not object_id(''log_ddl_bak'') is null drop table log_ddl_bak
        select * into log_ddl_bak from log_ddl
        drop table log_ddl
        end
    end -- upgrade log_ddl

-- drop table log_ddl
if object_id(''log_ddl'') is null
    begin
    -- drop table LOG_DDL
    create table LOG_DDL(
        svr int,                    -- fast and simple merge group
        id int,
        tid tinyint,
        rid int,                    -- parent id (tsql->obj->db) | app->host\usr
        pid int,                    -- host\usr->db(rid)         | app->...tsql...
        flags smallint,             -- at least 0
        skey as case
                when tid!=12        -- select code from tids
                then substring([txt],1,128)
                else null
                end,
        [key] int,                  -- crc32 of skey if tid!=code
        txt nvarchar(max) null,     -- tsql
        ev smallint null,           -- event
        rel int null,               -- release
        dt datetime,                -- at least getdate
        constraint PK_LOG_DDL primary key (id desc)
        )
    -- drop index  log_ddl.IX_LOG_DDL_KEY
    create index IX_LOG_DDL_KEY on log_ddl(tid,[key],dt desc)
    -- include (id) -- 100:13s:+21%
    include(id,svr,skey,rid)    -- 100:4s:+28%
    -- select id from log_ddl where tid=2 and [key]=2 and skey=''test''
    raiserror(''-- table log_ddl created'',10,1)
    end -- create table

if @log_ddl=1 goto ret

-- ====================================================================== cfg ==
-- select dbo.fn__script_sign(''cfg'',default)
if not object_id(''cfg'') is null
and dbo.fn__script_sign(''cfg'',default) in (
    1865897288.0000,2083733301.0000
    )
    begin
    print ''-- dropped old cfg ''
    drop table cfg
    end

if object_id(''cfg'') is null
    begin
    create table [dbo].[cfg] (
        [id] int not null  identity(1,1) ,
        [flags] smallint not null,
        [rid] int not null ,
        [key] sysname collate Latin1_General_CI_AS not null ,
        [val] sql_variant not null
    ) on [PRIMARY]

    alter table [dbo].[cfg] add constraint [pk_cfg] primary key clustered (
    [id] DESC
    ) on [PRIMARY]

    create unique nonclustered index [ix_cfg] on [dbo].[cfg](
    rid,[key]
    ) on [PRIMARY]

    select @sign=dbo.fn__script_sign(''cfg'',1) -- with idx

    print ''-- new cfg created with sign: ''+@sign
    end -- cfg
else
    print ''-- cfg already exists''

-- update fnd table or view (has the same sign)
if dbo.fn__script_sign(''fnd'',default)!=1649957971.0000
    begin
    exec sp__drop ''fnd''
    print ''-- fnd dropped''
    end
else
    begin
    -- test if fnd a view/synonym to a not existant table
    if not object_id(''fnd'') is null
        begin try
        exec(''declare @id int select top 0 @id=id from fnd'')
        print ''-- fnd of last version''
        end try
        begin catch
        exec sp__drop ''fnd''
        print ''-- fnd dropped because point to nothing''
        end catch
    end

if object_id(''script_act'') is null
    begin
    -- drop table script_act
    create table script_act(
        id int identity constraint pk_script_act primary key,
        rid int,    -- crc32(@proc)
        pid int,    -- crc32(@obj)
        idx int,    -- execution order
        txt nvarchar(4000)
        )
    create unique index ix_script_act_rid_pid on script_act(rid,pid)

    end -- script_act

if object_id(''sp__trace'') is null
and not object_id(''sp__log_trace'') is null
    begin
    print ''-- create synonym for sp__trace->sp__log_trace''
    create synonym sp__trace for sp__log_trace
    end

/* to check NET version
    xp_instance_regenumkeys ''HKEY_LOCAL_MACHINE'',
                            ''Software\Microsoft\NET Framework Setup\NDP''
*/

-- cause memory pressure this do not compile; must use AWE mode or have 64bit

-- ##########################
-- ##
-- ## do not comment hex code, see sp__script, comment 121212
-- ##
-- ########################################################
declare @asm varbinary(max)
select @asm=0x\\

4d5a90000300000004000000ffff0000b80000000000000040000000000000000000000000000000000000000000000000000000000000000000000080000000\\

0e1fba0e00b409cd21b8014ccd21546869732070726f6772616d2063616e6e6f742062652072756e20696e20444f53206d6f64652e0d0d0a2400000000000000\\

504500004c0103009c809b500000000000000000e00002210b0108000008000000060000000000001e2700000020000000400000000040000020000000020000\\

04000000000000000400000000000000008000000002000000000000030040850000100000100000000010000010000000000000100000000000000000000000\\

cc2600004f000000004000004803000000000000000000000000000000000000006000000c000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002e74657874000000\\

24070000002000000008000000020000000000000000000000000000200000602e72737263000000480300000040000000040000000a00000000000000000000\\

00000000400000402e72656c6f6300000c0000000060000000020000000e00000000000000000000000000004000004200000000000000000000000000000000\\

00270000000000004800000002000500642100006805000001000000000000000000000000000000000000000000000000000000000000000000000000000000\\

0000000000000000000000000000000013300400510000000100001100026f0400000a16fe01130411042d04020d2b3b026f0500000a0a730600000a0b071717\\

730700000a0c080616068e696f0800000a00086f0900000a00086f0a00000a00140c07730b00000a0d2b00092a0000001b300400820000000200001100026f04\\

00000a16fe01130611062d050213052b6a026f0c00000a1617730700000a0a170b20102700000c088d0a0000010d730600000a1304002b0d0011040916076f08\\

00000a0000060916086f0d00000a250b16fe02130611062ddf00de042600fe1a00de0c00066f0a00000a00140a00dc001104730b00000a13052b0011052a0000\\

011c000000003900276000040b000001020039002e67000c000000001e02280e00000a2a42534a4201000100000000000c00000076322e302e35303732370000\\

000005006c0000009c010000237e0000080200001802000023537472696e67730000000020040000080000002355530028040000100000002347554944000000\\

380400003001000023426c6f620000000000000002000001471502000900000000fa013300160000010000000b0000000200000003000000020000000e000000\\

0300000002000000010000000300000000000a00010000000000060056004f000a007e0069000600c900a9000600e900a9000a005401390106008e0184010e00\\

b1019b010600bf0184010e00c6019b01060002024f0006000c024f00000000000100000000000100010001001000400000000500010001005020000000009600\\

87000a000100b02000000000960094000a0002005c21000000008618a3001100030000000100690100000100e8011900a30015002100a30011002900a3001100\\

11006e01be0011007901c2003100a30011003900a300c7004100d601d0004100dc0111004100e20111001100a300d8001100f701ea0041000702ef000900a300\\

110020001b001a002e000b0005012e0013000e01de00f70004800000000000000000000000000000000007010000020000000000000000000000010046000000\\

000002000000000000000000000001005d000000000002000000000000000000000001004f00000000000000003c4d6f64756c653e0073705f5f617373656d62\\

6c795f45384346423742355f363842445f343130455f413944465f4631334539314332344546452e646c6c007574696c73006d73636f726c6962005379737465\\

6d004f626a6563740053797374656d2e446174610053797374656d2e446174612e53716c54797065730053716c427974657300666e5f5f636f6d707265737300\\

666e5f5f6465636f6d7072657373002e63746f720053797374656d2e52756e74696d652e436f6d70696c6572536572766963657300436f6d70696c6174696f6e\\

52656c61786174696f6e734174747269627574650052756e74696d65436f6d7061746962696c6974794174747269627574650073705f5f617373656d626c795f\\

45384346423742355f363842445f343130455f413944465f463133453931433234454645004d6963726f736f66742e53716c5365727665722e53657276657200\\

53716c46756e6374696f6e41747472696275746500626c6f62006765745f49734e756c6c006765745f4275666665720053797374656d2e494f004d656d6f7279\\

53747265616d0053797374656d2e494f2e436f6d7072657373696f6e004465666c61746553747265616d0053747265616d00436f6d7072657373696f6e4d6f64\\

6500577269746500466c75736800436c6f736500636f6d70726573736564426c6f62006765745f53747265616d0042797465005265616400457863657074696f\\

6e00000000032000000000001ee0c5882932964486d9ccf1d71714510008b77a5c561934e0890600011209120903200001042001010880a20100020054020f49\\

7344657465726d696e6973746963015455794d6963726f736f66742e53716c5365727665722e5365727665722e446174614163636573734b696e642c20537973\\

74656d2e446174612c2056657273696f6e3d322e302e302e302c2043756c747572653d6e65757472616c2c205075626c69634b6579546f6b656e3d6237376135\\

63353631393334653038390a4461746141636365737300000000032000020420001d05082003011221112502072003011d0508080520010112210b07051d0512\\

19121d1209020420001221072003081d0508080d0707121d08081d0512191209020801000800000000001e01000100540216577261704e6f6e45786365707469\\

6f6e5468726f777301000000f426000000000000000000000e270000002000000000000000000000000000000000000000000000002700000000000000000000\\

00005f436f72446c6c4d61696e006d73636f7265652e646c6c0000000000ff250020400000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000100100000001800008000000000000000000000000000000100010000003000008000000000000000000000000000000100\\

000000004800000058400000ec0200000000000000000000ec0234000000560053005f00560045005200530049004f004e005f0049004e0046004f0000000000\\

bd04effe00000100000000000000000000000000000000003f000000000000000400000002000000000000000000000000000000440000000100560061007200\\

460069006c00650049006e0066006f00000000002400040000005400720061006e0073006c006100740069006f006e00000000000000b0044c02000001005300\\

7400720069006e006700460069006c00650049006e0066006f0000002802000001003000300030003000300034006200300000002c0002000100460069006c00\\

65004400650073006300720069007000740069006f006e000000000020000000300008000100460069006c006500560065007200730069006f006e0000000000\\

30002e0030002e0030002e00300000008c003600010049006e007400650072006e0061006c004e0061006d0065000000730070005f005f006100730073006500\\

6d0062006c0079005f00450038004300460042003700420035005f0036003800420044005f0034003100300045005f0041003900440046005f00460031003300\\

4500390031004300320034004500460045002e0064006c006c0000002800020001004c006500670061006c0043006f0070007900720069006700680074000000\\

200000009400360001004f0072006900670069006e0061006c00460069006c0065006e0061006d0065000000730070005f005f0061007300730065006d006200\\

6c0079005f00450038004300460042003700420035005f0036003800420044005f0034003100300045005f0041003900440046005f0046003100330045003900\\

31004300320034004500460045002e0064006c006c000000340008000100500072006f006400750063007400560065007200730069006f006e00000030002e00\\

30002e0030002e003000000038000800010041007300730065006d0062006c0079002000560065007200730069006f006e00000030002e0030002e0030002e00\\

30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

002000000c0000002037000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\

00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

if (select value_in_use from sys.configurations where name like ''clr enabled'')=0 or
   (select value from sys.dm_clr_properties where name=''state'')
   =''CLR initialization permanently failed''
    print ''-- CLR disabled or CLR init failed''
else
    begin
    if not exists(select * from sys.assemblies where name=''utility_core'')
        begin try
        print ''-- creating assembly utility core''
        -- drop assembly utility_core
        create assembly utility_core
        from @asm
        with permission_set = safe

        exec(''
        create function [fn__compress] (@blob varbinary(max))
        returns varbinary(max)
        as external name utility_core.utils.fn__compress;'')
        exec(''
        create function [fn__decompress] (@compressedblob varbinary(max))
        returns varbinary(max)
        as external name utility_core.utils.fn__decompress;'')
        end try
        begin catch
        -- select @err_msg=error_message()
        print ''-- assembly utility core failed with msg:''
        print isnull(error_message(),''???'')
        end catch -- assembly utility_core
    end -- clr tests

-- ##########################
-- ##
-- ## end
-- ##
-- ########################################################
/*
if @end=1
    begin
    end
*/
goto ret

-- =================================================================== errors ==
/*
err_sample1:
exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param
goto ret
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    create or upgrade base system objects

Parameters
    @opt    options
            run         run the inside code
            log_ddl     init only this table (called by sp__script_trace_db)

''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__utility_setup'
exec sp__utility_setup @opt='run|begin'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__utility_setup:

-- ====================================================================== fn__at
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__at',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__at') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__at') with nowait
        goto skip_fn__at
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__at') with nowait
        goto skip_fn__at
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__at') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__at'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__at]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091018\s.zaglio: added null control: nb: problem on '' '', see tests
    v:090211/S.Zaglio: optimized
    v:081110.0100/S.Zaglio: expanded @seps to nvarchar(32)
    v:080801/S.Zaglio: managed bug on tokens count and added @sep
    v:080730/S.Zaglio: managed bug on empty strings
    v:080714/S.Zaglio: return token position into tokens: b(a|b|c) -> 2
    t:print dbo.fn__at(''a'',''a|b|c'',''|'') print dbo.fn__at(''d'',''a|b|c'',''|'')  -->1 & 0
    t:print dbo.fn__at(''a'',''b'',''|'') -->0
    t:print dbo.fn__at(''a'','''',''|'') -->0
    t:print dbo.fn__at(''a b c'',''b'','''') -->0   -- NB! infinite loop
    t:print dbo.fn__at('''',''a|b|c'',''|'') -->0
    t:print isnull(dbo.fn__str_at(''a|b|c'',''|'',4),''(null)'') -->null
    t:print isnull(dbo.fn__str_at(''a'',''|'',3),''(null)'') --> null
    t:print isnull(dbo.fn__str_at(''a|'',''|'',3),''(null)'') -->null
    c:very slow. Must be optimized
*/
CREATE function fn__at(
    @token sysname,
    @tokens nvarchar(4000),
    @sep nvarchar(32)=''|''
    )
returns int
as
begin
declare @i int
if not @tokens is null
    select @i=pos from dbo.fn__str_table(@tokens,@sep) where token=@token
return coalesce(@i,0)
/*
-- old version
declare @n int
declare @st sysname
set @i=1
set @n=dbo.fn__str_count(@tokens,@sep)
while (@i<=@n) begin
    set @st=dbo.fn__str_at(@tokens,@sep,@i)
    if @st=@token return @i
    if @st is null break
    set @i=@i+1
end -- while
return 0
*/
end -- fn__at'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__at:

-- ============================================================ fn__base64decode
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__base64decode',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120123
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__base64decode') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__base64decode') with nowait
        goto skip_fn__base64decode
        end
    if @ver>120123
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__base64decode') with nowait
        goto skip_fn__base64decode
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__base64decode') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__base64decode'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__base64decode]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120123\s.zaglio: convert a nvarchar with base 64 to text
    t:select dbo.fn__base64decode(''VABlAHMAdAAgAEQAYQB0AGEA'') -- Test Data
*/
CREATE function dbo.fn__base64decode
(
    @base64 nvarchar(max)
)
returns nvarchar(max)
as
begin
    declare @bin nvarchar(max)
    select @bin=
    cast(cast(N'''' as xml).value(
            ''xs:base64Binary(sql:variable("@base64"))'',
            ''varbinary(max)'')
         as nvarchar(max))

    return @bin
end  -- fn__base64decode'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__base64decode:

-- ============================================================ fn__base64encode
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__base64encode',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120123
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__base64encode') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__base64encode') with nowait
        goto skip_fn__base64encode
        end
    if @ver>120123
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__base64encode') with nowait
        goto skip_fn__base64encode
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__base64encode') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__base64encode'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__base64encode]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120123\s.zaglio: convert a nvarchar to base 64 code
    t:select dbo.fn__base64encode(''Test Data'') -- VABlAHMAdAAgAEQAYQB0AGEA
*/
CREATE function dbo.fn__base64encode
(
    @text nvarchar(max)
)
returns nvarchar(max)
as
begin
    declare @bin varchar(max)
    select @bin=
        cast(N'''' as xml).value(
                ''xs:base64Binary(xs:hexBinary(sql:column("bin")))'',
                ''VARCHAR(MAX)''
             )
    from (select cast(@text as varbinary(max)) as bin) as tmp;
    return @bin
end  -- fn__base64encode'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__base64encode:

-- ======================================================= fn__binaryfloat2float
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__binaryfloat2float',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120904
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__binaryfloat2float') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__binaryfloat2float') with nowait
        goto skip_fn__binaryfloat2float
        end
    if @ver>120904
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__binaryfloat2float') with nowait
        goto skip_fn__binaryfloat2float
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__binaryfloat2float') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__binaryfloat2float'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__binaryfloat2float]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:binary,real,convert,c
    v:120904\s.zaglio: from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=81849
*/
create function [dbo].[fn__binaryfloat2float]
(
    @binaryfloat binary(8)
)
returns float
as
begin
    return    sign(cast(@binaryfloat as bigint))
        * (1.0 + (cast(@binaryfloat as bigint) & 0x000fffffffffffff) * power(cast(2 as float), -52))
        * power(cast(2 as float), (cast(@binaryfloat as bigint) & 0x7ff0000000000000) / 0x0010000000000000 - 1023)
end -- [fn__binaryfloat2float]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__binaryfloat2float:

-- ========================================================= fn__binaryreal2real
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__binaryreal2real',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120904
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__binaryreal2real') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__binaryreal2real') with nowait
        goto skip_fn__binaryreal2real
        end
    if @ver>120904
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__binaryreal2real') with nowait
        goto skip_fn__binaryreal2real
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__binaryreal2real') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__binaryreal2real'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__binaryreal2real]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:binary,real,convert,c
    v:120904\s.zaglio: from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=81849
*/
create function [dbo].[fn__binaryreal2real]
(
    @binaryfloat binary(4)
)
returns real
as
begin
    return    sign(cast(@binaryfloat as int))
        * (1.0 + (cast(@binaryfloat as int) &  0x007fffff) * power(cast(2 as real), -23))
        * power(cast(2 as real), (cast(@binaryfloat as int) & 0x7f800000) / 0x00800000 - 127)
end -- fn__binaryreal2real'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__binaryreal2real:

-- ================================================================== fn__breset
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__breset',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090428
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__breset') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__breset') with nowait
        goto skip_fn__breset
        end
    if @ver>090428
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__breset') with nowait
        goto skip_fn__breset
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__breset') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__breset'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__breset]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090428\S.Zaglio: provided only for study
    t:print dbo.fn__breset(6,2) -->4
*/
CREATE function fn__breset(@src int, @val int)
returns int
as
begin
return @src & (~@val)
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__breset:

-- ==================================================================== fn__bset
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__bset',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090428
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__bset') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__bset') with nowait
        goto skip_fn__bset
        end
    if @ver>090428
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__bset') with nowait
        goto skip_fn__bset
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__bset') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__bset'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__bset]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090428\S.Zaglio: provided only for study
    t:print dbo.fn__bset(4,2) -->6
*/
CREATE function fn__bset(@src int, @val int)
returns int
as
begin
return @src | @val
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__bset:

-- =============================================================== fn__charcount
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__charcount',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100204
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__charcount') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__charcount') with nowait
        goto skip_fn__charcount
        end
    if @ver>100204
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__charcount') with nowait
        goto skip_fn__charcount
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__charcount') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__charcount'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__charcount]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100204\s.zaglio: count char occurrence
    todo:deprecate to fn__occurrences
    t:print dbo.fn__charcount(''.'',''schema.table.fld'')
    t:print dbo.fn__charcount(''.'',''schema_table_fld'')
    t:print dbo.fn__charcount(''.'',null)
*/
CREATE function [dbo].[fn__charcount](@char nchar(1),@string nvarchar(4000))
returns int
as
begin
declare @i int,@nc int
if @string is null or @char is null return null
select @i=charindex(@char,@string),@nc=0
while (@i>0)
    begin
    select @nc=@nc+1
    select @i=charindex(@char,@string,@i+1)
    end
return @nc
end -- end fn__charindex'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__charcount:

-- =============================================================== fn__charindex
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__charindex',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130902
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__charindex') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__charindex') with nowait
        goto skip_fn__charindex
        end
    if @ver>130902
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__charindex') with nowait
        goto skip_fn__charindex
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__charindex') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__charindex'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__charindex]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130902,130830\s.zaglio: a bug when len(@what)>1;optimized from 2x to 4x
    v:111223\s.zaglio: upgraded to int indexes
    v:111111\s.zaglio: managed search of '' '' backwards
    v:091217\s.zaglio: a bug on right 1st char
    v:091015\s.zaglio: as charindex but can search from end to begin
    t:print dbo.fn__charindex(''\'',''c:\test\test2'',0) -- null
    t:print dbo.fn__charindex(''\'',''c:\test\test2'',-1) -- 8
    t:print dbo.fn__charindex(''\'',''c:\test\test2'',-9) -- 3
    t:print dbo.fn__charindex(''\'',null,-1) -- null
    t:print charindex(''\'',null) -- null
    t:print dbo.fn__charindex('']'',''end]'',-1) -- 4
    t:print dbo.fn__charindex('' '',''left rght'',-1) -- 5
    t:print dbo.fn__charindex(''\'',''nothing do find'',-1) -- 0
    t:print dbo.fn__charindex('' from '',''select * from #objs '',-1) -- 9
*/
CREATE function fn__charindex(
    @what nvarchar(4000),
    @where nvarchar(4000),
    @from int
    )
returns int
as
begin
declare @i int,@n int,@m int
if @from>=0 return null -- prevent use of this instead of charindex
select @from=-@from,@n=len(''.''+@where+''.'')-2,@m=len(''.''+@what+''.'')-2
select @i=charindex(reverse(@what),reverse(@where),@from)
if @i>0 return @n-@i+1-@m+1
return 0
end -- end fn__charindex'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__charindex:

-- =============================================================== fn__chk_email
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__chk_email',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121004.1424
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__chk_email') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__chk_email') with nowait
        goto skip_fn__chk_email
        end
    if @ver>121004.1424
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__chk_email') with nowait
        goto skip_fn__chk_email
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__chk_email') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__chk_email'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__chk_email]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:121004.1424\s.zaglio: accept empty email when uses @seps
    v:121004\s.zaglio: changed fn__chk_str to fn__str_chk and used multi @seps
    v:110720\s.zaglio: added null/empty test
    v:110408\s.zaglio: return wrong emails instead of a boolean
    v:110219\s.zaglio: check if @email is correct and return wrongs
    t:print dbo.fn__chk_email(''me.you@me@you'',default)  -- ko
    t:print dbo.fn__chk_email(''me.you@m-e'',default)     -- null
    t:print dbo.fn__chk_email(''me@#me'',default)         -- ko
    t:print dbo.fn__chk_email(''me@me;you@you'','';'')      -- null
    t:print dbo.fn__chk_email(''me@me;you@U#U'','';'')      -- ko
    t:print dbo.fn__chk_email(''me.youm-e'',default)      -- ko
    t:print dbo.fn__chk_email(''me@you.com'',default)     -- ok
    t:print dbo.fn__chk_email(''ok@ok.ok;me.youm-e;ok@ok.ok'','';'')      -- ko
    t:print dbo.fn__chk_email(''ok1@ok.ok;ok2@ok.ok;'','';,'')      -- ok
    t:print dbo.fn__chk_email(''ok1@ok.ok;;'','';,'')      -- ok
    t:sp__find ''fn__chk_email''
*/
CREATE function fn__chk_email(@email nvarchar(4000),@seps nvarchar(32))
returns nvarchar(4000)
as
begin
declare
    @chars nvarchar(32),@token nvarchar(1024),@wrong nvarchar(1024),
    @i int
select @chars=''-a-z0-9_.@'' -- !#$%&''*+-/=?^_`{|}~

-- empty or null email is an error (forgotten?)
if isnull(@email,'''')='''' return ''''

if @seps is null
    begin
    if dbo.fn__str_count(@email,''@'')!=2
    or dbo.fn__str_chk(@email,@chars)=0 -- not exists
        return @email
    end -- single mail

select @i=1
while 1=1
    begin
    select @token=dbo.fn__str_parse(@email,@seps,@i)
    -- select dbo.fn__str_parse(''ok1@ok.ok;ok2@ok.ok;'','';,'',1)
    if @token is null break
    if @token!=''''
    and (dbo.fn__str_count(@token,''@'')!=2
         or dbo.fn__str_chk(@token,@chars)!=1
        )
        select @wrong=isnull(@wrong+left(@seps,1),'''')+@token
    select @i=@i+1
    end -- while of cursor

return @wrong
end -- fn__chk_email'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__chk_email:

-- ================================================================= fn__comment
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__comment',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100328
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__comment') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__comment') with nowait
        goto skip_fn__comment
        end
    if @ver>100328
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__comment') with nowait
        goto skip_fn__comment
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__comment') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__comment'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__comment]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100328\s.zaglio: adapted to fn__comment_types
    v:100204\s.zaglio: return comment of obj
*/
CREATE function [dbo].[fn__comment](@path sysname)
returns nvarchar(4000)
as
begin
declare
    @schema sysname,@prop sysname,
    @obj sysname,@obj_type sysname,
    @col sysname,@col_type sysname,
    @comment nvarchar(4000),@id int,
    @xtype nvarchar(2),@sch_type sysname

select @prop=prop,@schema=sch,@sch_type=sch_type,
       @obj_type=obj_type,@obj=obj,
       @col_type=sub_type,@col=sub
from dbo.fn__comment_types(@path)

select @comment=convert(nvarchar(4000),value)
from fn_listextendedproperty (
    @prop,
    @sch_type, @schema,
    @obj_type, @obj,
    @col_type, @col);

return @comment
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__comment:

-- ================================================================== fn__config
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__config',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131117
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__config') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__config') with nowait
        goto skip_fn__config
        end
    if @ver>131117
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__config') with nowait
        goto skip_fn__config
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__config') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__config'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__config]

begin try
exec dbo.sp_executesql @statement = N'   /*  leave this
    l:see LICENSE file
    g:utility
    d:131117\s.zaglio: sp__config
    v:131117\s.zaglio: adapted to new cfg
    v:101019\s.zaglio: added autogeneration from sp__config.
    v:100424\s.zaglio: added inline-inmemory variables manag.
    v:100405\s.zaglio: uses of table if exists
    v:081218\S.Zaglio: usefull for personalization
    t:select dbo.fn__config(''smtp_server'',null)
    t:insert cfg select 0,0,''smtp_server'',''10.0.0.148''
*/
CREATE function [dbo].[fn__config](@var sysname,@default sql_variant=null)
returns sql_variant
as
begin
declare @v sql_variant
if left(@var,4)=''mem.''
    begin
    select @var=substring(@var,5,128)
    select @v=
      case @var
      when ''mem.test1'' then ''mem.test1'' -- memory var test
      when ''mem.test'' then ''mem.test'' -- memory var test
      else null
      end -- case
    return @v
    end
if object_id(''cfg'') is null return @default
if left(@var,1) like ''[0-9]''
    select @v=val from cfg where id=@var
else
    select @v=val from cfg where [key]=@var
return isnull(@v,@default)
end -- fn__config'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__config:

-- ============================================================== fn__config_app
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__config_app',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131125
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__config_app') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__config_app') with nowait
        goto skip_fn__config_app
        end
    if @ver>131125
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__config_app') with nowait
        goto skip_fn__config_app
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__config_app') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__config_app'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__config_app]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:config,utility,application,fn_config,fn__config
    v:131125\s.zaglio: when not find into _config, do NOT search in __config
    v:131122\s.zaglio: mixed and moved code from sp__job_status
    r:131117\s.zaglio: multiple config search
    t:select dbo.fn__config_app(''.test'',''nop'')                      -- from _ or nop
    t:select dbo.fn__config_app(''.test|test'',''nop'')                 -- from _
    t:select dbo.fn__config_app(''.test1|noexists1|test1'',''nop'')     -- from __
    t:select dbo.fn__config_app(''.test1|noexists1|noexists2'',''nop'') -- nop
    t:select dbo.fn__config(''test'',''nop''),dbo.fn__config(''test1'',''nop'')
    t:drop function fn_config
    t:select dbo.fn__config_app(''.test|test'',''nop'')                 -- from __
*/
CREATE function fn__config_app(@vars nvarchar(256), @default sql_variant)
returns sql_variant
as
begin
/*
drop function fn_config
create function fn_config(@param sysname,@defval sysname)
returns sysname as begin declare @s sysname
    select @s=isnull(case @param when ''TEST'' then ''from _'' else null end,@defval)
return @s
end
*/
declare @v sql_variant,@var sysname,@fn_id int
select @fn_id=isnull(object_id(''fn_config''),0)
-- do a minimal check to ensure a sign compatibility with
-- generic application FN_CONFIG
declare cs cursor local for select token from fn__str_split(@vars,''|'')
open cs
while 1=1
    begin
    fetch next from cs into @var
    if @@fetch_status!=0 break
    if left(@var,1)=''.''
        begin
        select @var=substring(@var,2,256)
        if @fn_id!=0
            select @v=dbo.fn_config(@var,cast(@default as nvarchar(4000)))
        end
    else
        select @v=dbo.fn__config(@var,@default)

    if @v=@default select @v=null
    if not @v is null break
    end -- cursor cs
close cs
deallocate cs

select @v=isnull(@v,@default)
return @v
end  -- fn__config_app'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__config_app:

-- ============================================================ fn__context_info
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__context_info',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120724
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__context_info') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__context_info') with nowait
        goto skip_fn__context_info
        end
    if @ver>120724
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__context_info') with nowait
        goto skip_fn__context_info
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__context_info') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__context_info'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__context_info]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120724\s.zaglio: return position of @val into context else 0
*/
CREATE function fn__context_info(@val sysname)
returns int
as
begin
declare @i int,@info varbinary(128),@code binary(2)
select @code=dbo.fn__crc16(@val)
select @info=context_info(),@i=1
while (@i<len(@info))
    begin
    if substring(@info,@i,2)=@code return @i
    select @i=@i+2
    end
return 0
end -- fn__context_info'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__context_info:

-- =================================================================== fn__count
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__count',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120116
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__count') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__count') with nowait
        goto skip_fn__count
        end
    if @ver>120116
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__count') with nowait
        goto skip_fn__count
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__count') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__count'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__count]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120116\s.zaglio: fast row cnt. function version of sp__count
    t:select dbo.fn__count(''cfg'')
*/
CREATE function fn__count(@tbl sysname)
returns bigint
as
begin
declare @n bigint
--SQL Server 2000+ (work well also in 2k5)
SELECT @n= si.rowcnt
FROM sysindexes si
JOIN sysobjects so ON si.id = so.id
WHERE si.indid < 2  --clustered index or heap
AND OBJECTPROPERTY(so.id, ''IsMSShipped'') = 0
AND so.xtype=''u''
and so.name=@tbl

/* SQL Server 2005+  give also schema
-- originally from
-- http://developerspla.net/2010/sql-server/tsql/row-count-of-all-tables/
SELECT [TableName]  = OBJECT_SCHEMA_NAME(sp.[object_id]) + ''.''
                    + OBJECT_NAME(sp.[object_id])
            ,[RowCount]     = SUM(sp.rows)
FROM        sys.partitions sp
WHERE       OBJECT_SCHEMA_NAME(sp.[object_id]) <> ''sys''
AND         sp.index_id < 2
GROUP BY    object_id
ORDER BY    [RowCount] DESC
*/
return @n
end -- fn__count'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__count:

-- =================================================================== fn__crc16
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__crc16',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130901
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__crc16') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__crc16') with nowait
        goto skip_fn__crc16
        end
    if @ver>130901
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__crc16') with nowait
        goto skip_fn__crc16
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__crc16') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__crc16'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__crc16]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    d:130202\s.zaglio: fn__crc16_tbl
    v:130901\s.zaglio: correction for unicode>32768
    v:130202\s.zaglio: embedded lookup table, 5 times faster
    v:090614\S.Zaglio: used by fn_crc16
    t:
        print dbo.fn__crc16('''')         -- 0
        print dbo.fn__crc16(''a'')        -- 22398
        print dbo.fn__crc16(''b'')        -- 22078
        print dbo.fn__crc16(''aa'')       -- -30633
        print dbo.fn__crc16(''ab'')       -- -30441
        print dbo.fn__crc16(''ba'')       -- 30805
        print dbo.fn__crc16(N''ウィ'')    -- -30571
*/
CREATE function [dbo].[fn__crc16](
    @string nvarchar(4000)
)
returns smallint
as
begin
declare @lookup binary(512)
select @lookup = 0x\\

0000C0C1C1810140C30103C00280C241C60106C00780C7410500C5C1C4810440CC010CC00D80\\

CD410F00CFC1CE810E400A00CAC1CB810B40C90109C00880C841D80118C01980D9411B00DBC1\\

DA811A401E00DEC1DF811F40DD011DC01C80DC411400D4C1D5811540D70117C01680D641D201\\

12C01380D3411100D1C1D0811040F00130C03180F1413300F3C1F28132403600F6C1F7813740\\

F50135C03480F4413C00FCC1FD813D40FF013FC03E80FE41FA013AC03B80FB413900F9C1F881\\

38402800E8C1E9812940EB012BC02A80EA41EE012EC02F80EF412D00EDC1EC812C40E40124C0\\

2580E5412700E7C1E68126402200E2C1E3812340E10121C02080E041A00160C06180A1416300\\

A3C1A28162406600A6C1A7816740A50165C06480A4416C00ACC1AD816D40AF016FC06E80AE41\\

AA016AC06B80AB416900A9C1A88168407800B8C1B9817940BB017BC07A80BA41BE017EC07F80\\

BF417D00BDC1BC817C40B40174C07580B5417700B7C1B68176407200B2C1B3817340B10171C0\\

7080B041500090C191815140930153C052809241960156C057809741550095C1948154409C01\\

5CC05D809D415F009FC19E815E405A009AC19B815B40990159C058809841880148C049808941\\

4B008BC18A814A404E008EC18F814F408D014DC04C808C41440084C185814540870147C04680\\

8641820142C043808341410081C180814040

declare @crc smallint
declare @c tinyint
declare @t tinyint
declare @a smallint
declare @i smallint
declare @u int
set @a=0
set @crc = 0xFFFF
set @i=1
declare @l int
set @l=len(@string)

while (@i<=@l) begin
    set @u=unicode(substring(@string,@i,1))

    set @c=@u % 256
    set @t=(@crc^@c)&0xff
    set @a=@crc/256
    set @crc=@a ^ substring(@lookup,@t*2+1,2)

    set @c=@u / 256
    if @c!=0 begin -- this keep compatibility with old nvarchar crc32
                   -- and also the whole performances
        set @t=(@crc^@c)&0xff
        set @a=@crc/256
        set @crc=@a ^ substring(@lookup,@t*2+1,2)
        end

    set @i=@i+1
end
set @crc= ~@crc
return @crc
end -- fn__crc16'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__crc16:

-- =================================================================== fn__crc32
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__crc32',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130901
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__crc32') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__crc32') with nowait
        goto skip_fn__crc32
        end
    if @ver>130901
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__crc32') with nowait
        goto skip_fn__crc32
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__crc32') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__crc32'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__crc32]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130901\s.zaglio: correct a bug near unicode>32768
    v:130202\s.zaglio: embedded lookup table, 5 times faster
    d:130202\s.zaglio: fn__crc32_tbl
    v:110315\s.zaglio: @string to ntext
    v:090930\s.zaglio: a bug on the name of tbl
    v:090614\s.zaglio: calculate crc32 of a unicode & non unicode string
    t:
        select
            st,res,dbo.fn__crc32(st) crc,
            case when dbo.fn__crc32(st)=res then ''ok'' else ''ko'' end st
        from (
            select ''jhon'' st,2071226836 res union
            select N''Джон'',877079858 union
            select ''j'',2137352139 union
            select N''Д'',1746534835 union
            select N''�'',-842440062
        ) data
    t:
        -- mega test
        declare @v nvarchar(max),@i int
        select @i=10000,@v=''start\n''
        while (@i>0) select @v=@v+''01234567890\n'',@i=@i-1
        select @v=@v+''end''
        print datalength(@v)        -- 260020
        declare @d datetime select @d=getdate()
        print dbo.fn__crc32(@v)     -- 531861221
        exec sp__elapsed @d,''''      -- 440ms
*/
CREATE function [dbo].[fn__crc32](
    @string nvarchar(max)
    )
returns int
as
begin
declare @crc int
declare @u int      -- to containt an unsigned smallint
declare @c tinyint
declare @t tinyint
declare @a int
declare @i int
declare @lookup varbinary(2048)
select @lookup = 0x\\

0000000077073096ee0e612c990951ba076dc419706af48fe963a5359e6495a30edb8832\\

79dcb8a4e0d5e91e97d2d98809b64c2b7eb17cbde7b82d0790bf1d911db710646ab020f2\\

f3b9714884be41de1adad47d6ddde4ebf4d4b55183d385c7136c9856646ba8c0fd62f97a\\

8a65c9ec14015c4f63066cd9fa0f3d638d080df53b6e20c84c69105ed56041e4a2677172\\

3c03e4d14b04d447d20d85fda50ab56b35b5a8fa42b2986cdbbbc9d6acbcf94032d86ce3\\

45df5c75dcd60dcfabd13d5926d930ac51de003ac8d75180bfd0611621b4f4b556b3c423\\

cfba9599b8bda50f2802b89e5f058808c60cd9b2b10be9242f6f7c8758684c11c1611dab\\

b6662d3d76dc419001db710698d220bcefd5102a71b1858906b6b51f9fbfe4a5e8b8d433\\

7807c9a20f00f9349609a88ee10e98187f6a0dbb086d3d2d91646c97e6635c016b6b51f4\\

1c6c6162856530d8f262004e6c0695ed1b01a57b8208f4c1f50fc45765b0d9c612b7e950\\

8bbeb8eafcb9887c62dd1ddf15da2d498cd37cf3fbd44c654db261583ab551cea3bc0074\\

d4bb30e24adfa5413dd895d7a4d1c46dd3d6f4fb4369e96a346ed9fcad678846da60b8d0\\

44042d7333031de5aa0a4c5fdd0d7cc95005713c270241aabe0b1010c90c20865768b525\\

206f85b3b966d409ce61e49f5edef90e29d9c998b0d09822c7d7a8b459b33d172eb40d81\\

b7bd5c3bc0ba6cadedb883209abfb3b603b6e20c74b1d29aead547399dd277af04db2615\\

73dc1683e3630b1294643b840d6d6a3e7a6a5aa8e40ecf0b9309ff9d0a00ae277d079eb1\\

f00f93448708a3d21e01f2686906c2fef762575d806567cb196c36716e6b06e7fed41b76\\

89d32be010da7a5a67dd4accf9b9df6f8ebeeff917b7be4360b08ed5d6d6a3e8a1d1937e\\

38d8c2c44fdff252d1bb67f1a6bc57673fb506dd48b2364bd80d2bdaaf0a1b4c36034af6\\

41047a60df60efc3a867df55316e8eef4669be79cb61b38cbc66831a256fd2a05268e236\\

cc0c7795bb0b4703220216b95505262fc5ba3bbeb2bd0b282bb45a925cb36a04c2d7ffa7\\

b5d0cf312cd99e8b5bdeae1d9b64c2b0ec63f226756aa39c026d930a9c0906a9eb0e363f\\

720767850500571395bf4a82e2b87a147bb12bae0cb61b3892d28e9be5d5be0d7cdcefb7\\

0bdbdf2186d3d2d4f1d4e24268ddb3f81fda836e81be16cdf6b9265b6fb077e118b74777\\

88085ae6ff0f6a7066063bca11010b5c8f659efff862ae69616bffd3166ccf45a00ae278\\

d70dd2ee4e0483543903b3c2a7672661d06016f74969474d3e6e77dbaed16a4ad9d65adc\\

40df0b6637d83bf0a9bcae53debb9ec547b2cf7f30b5ffe9bdbdf21ccabac28a53b39330\\

24b4a3a6bad03605cdd7069354de572923d967bfb3667a2ec4614ab85d681b022a6f2b94\\

b40bbe37c30c8ea15a05df1b2d02ef8d

select @a=0
select @crc = 0xFFFFFFFF
select @i=1
declare @l int
select @l=datalength(@string)/2

while (@i<=@l)
    begin
    -- select unicode(N''�'')/256
    select @u=unicode(substring(@string,@i,1))

    select @c=@u % 256
    select @t=(@crc & 0xff) ^ @c
    select @a=(@crc & 0x7FFFFFFF)/256
    if (@crc & 0x80000000)<>0 select @a=@a | 0x800000
    select @crc=@a ^ substring(@lookup,@t*4+1,4)

    select @c=@u / 256
    if @c!=0 begin -- this keep compatibility with old nvarchar crc32 and also the whole performances
        select @t=(@crc & 0xff) ^ @c
        select @a=(@crc & 0x7FFFFFFF)/256
        if (@crc & 0x80000000)<>0 select @a=@a | 0x800000
        select @crc=@a ^ substring(@lookup,@t*4+1,4)
        end -- zero char
    select @i=@i+1
    end
select @crc= @crc ^ 0xFFFFFFFF
return @crc
end -- [fn__crc32]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__crc32:

-- ==================================================================== fn__date
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__date',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131117
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__date') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__date') with nowait
        goto skip_fn__date
        end
    if @ver>131117
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__date') with nowait
        goto skip_fn__date
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__date') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__date'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__date]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:date,without,time,getdate,convert,number
    s:fn__time
    v:131117\s.zaglio: out a int instead of a date to align to fn__time
    v:130828\s.zaglio: return a date without time (more a reminder that to use everyday)
    t:select dbo.fn__date(getdate())
    t:
        select
            -- round a date to the nearest quarter
            cast(floor(cast(getdate() as float(53))*24*4)/(24*4) as datetime),
            -- faster but untill y5500; there was rounding issues at 30 and 00
            dateadd(minute, datediff(minute,0,getdate()) / 15 * 15, 0),
            -- last 15 minute increment, not the nearest
            cast(round(floor(cast(getdate() as float(53))*24*4)/(24*4),5) as smalldatetime)
*/
CREATE function fn__date(@dt datetime)
returns int
as
begin
return cast(convert(varchar(8),dateadd(dd, datediff(dd, 0, getdate())+0, 0),112) as int)
end -- fn__date'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__date:

-- ================================================================= fn__dateadd
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__dateadd',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=101008
        begin
        if @aut!='g.sorgente'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__dateadd') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__dateadd') with nowait
        goto skip_fn__dateadd
        end
    if @ver>101008
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__dateadd') with nowait
        goto skip_fn__dateadd
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__dateadd') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__dateadd'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__dateadd]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:101008\g.sorgente: add value to a date, avoiding overflow
    t:
        declare @d datetime
        set @d=getdate()
        print dbo.fn__dateadd(@d,5)
        print dbo.fn__dateadd(@d,-5)
        set @d=''9999-12-30T23:59:59.998''
        print dbo.fn__dateadd(@d,5)
*/
create function dbo.fn__dateadd (
     @dt   datetime=null
    ,@days int=null
) returns datetime
as
begin
declare @dtmax datetime
set @dtmax=convert(datetime,''9999-12-31T23:59:59.998'')

if (@days<0) set @dt=dateadd(day,@days,@dt)
else
    begin
    -- evito l''overflow verificando di non superare il 9999-12-31
    if @dt<=dateadd(day,-@days,@dtmax) set @dt=dateadd(day,@days,@dt)
    else set @dt=@dtmax
    end
return @dt
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__dateadd:

-- ======================================================== fn__dt_first_weekday
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__dt_first_weekday',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131117
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__dt_first_weekday') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__dt_first_weekday') with nowait
        goto skip_fn__dt_first_weekday
        end
    if @ver>131117
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__dt_first_weekday') with nowait
        goto skip_fn__dt_first_weekday
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__dt_first_weekday') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__dt_first_weekday'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__dt_first_weekday]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:date,first,day,week,set DATEFIRST
    c:http://stackoverflow.com/questions/7168874/get-first-day-of-week-in-sql-server
    c:more a reminder that to use everyday
    v:131117\s.zaglio: get 1st day of week
    t:select dbo.fn__dt_first_weekday(getdate())
*/
CREATE function fn__dt_first_weekday( -- always the datefirst weekday
    @d smalldatetime
)
returns smalldatetime
as
begin
    return (select dateadd(day, 1-datepart(weekday, @d), @d));
end -- fn__dt_first_weekday'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__dt_first_weekday:

-- ======================================================= fn__dt_sunday_of_week
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__dt_sunday_of_week',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131117
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__dt_sunday_of_week') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__dt_sunday_of_week') with nowait
        goto skip_fn__dt_sunday_of_week
        end
    if @ver>131117
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__dt_sunday_of_week') with nowait
        goto skip_fn__dt_sunday_of_week
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__dt_sunday_of_week') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__dt_sunday_of_week'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__dt_sunday_of_week]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:date,first,day,sunday,set datefirst
    c:http://stackoverflow.com/questions/7168874/get-first-day-of-week-in-sql-server
    c:more a reminder that to use everyday
    v:131117\s.zaglio: get 1st sunday of week
    t:select dbo.fn__dt_sunday_of_week(getdate())
*/
create function dbo.fn__dt_sunday_of_week
(
    @d smalldatetime
)
returns smalldatetime
as
begin
    return (select dateadd(week, datediff(week, ''19050101'', @d), ''19050101''));
end -- fn__dt_sunday_of_week'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__dt_sunday_of_week:

-- ================================================================== fn__exists
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__exists',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__exists') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__exists') with nowait
        goto skip_fn__exists
        end
    if @ver>100919.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__exists') with nowait
        goto skip_fn__exists
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__exists') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__exists'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__exists]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100919.1000\s.zaglio: added type "fl" for field
    v:100612\s.zaglio: better code; NB: for files/dir, use sp__dir
    v:091001\S.Zaglio: added management of #t
    v:090915\S.Zaglio: forwarded fkey test from rkey to fkey on sysfkeys
    v:090815\S.Zaglio: a remake using object_id()
    v:090813\S.Zaglio: manage of [quoted] names
    v:090705\S.Zaglio: added management of global temp tables
    v:090130\S.Zaglio: added db management in names (loosed type check)
    v:081217\S.Zaglio: added multiple check with and or or and (nolock)
    v:081207\S.Zaglio: added foreign keys check if @type=''fk''
    v:081021\S.Zaglio: added owner
    v:080808\S.Zaglio: check existance of obj of type. If type is null, find any
    t:print object_id(''dbo.sysobjects'')
    t: print dbo.fn__exists(''[sysobjects]'',null) -->1
    t: print dbo.fn__exists(''sysobjects'',null) -->1
    t: print dbo.fn__exists(''dbo.sysobjects'',null) -->1
    t: print dbo.fn__exists(''dbo.sysobjects'',''X'') -->1
    t: print dbo.fn__exists(''sys.sysobjects'',null) -->1
    t: print dbo.fn__exists(''nothing.sysobjects'',null) -->0
    t: print dbo.fn__exists(''sysobjects,syscolumns'',null) -->1 and condition
    t: print dbo.fn__exists(''sysobjects|sysnothing'',null) -->1 or condition
    t: print dbo.fn__exists(''sysobjects,sysnothing'',null) -->0 and condition
    t: create table ##test(id int) print dbo.fn__exists(''##test'',null) exec sp__drop ''##test''   -- 1
    t: create table #test(id int) print dbo.fn__exists(''#test'',null) exec sp__drop ''#test''      -- 1
    t:
        create table test_fn_exists(a int)
        print dbo.fn__exists(''test_fn_exists'',default)
        print dbo.fn__exists(''test_fn_exists.a'',''fl'')
        print dbo.fn__exists(''test_fn_exists.notex'',''fl'')
        drop table test_fn_exists
*/
CREATE function [dbo].[fn__exists](
    @objects nvarchar(4000),
    @type nvarchar(2) = null
    )
returns tinyint
as
begin
-- note: object_id uses dbo as default when not specified
-- select @objects=''[sysobjects]'',@type=null    <---- BE CAREFULL
--  declare @dbg bit,@objects nvarchar(4000), @type nvarchar(2) select @objects=''test_fn_exists.a'',@dbg=1

declare @stdout table(lno int identity,line nvarchar(4000))
declare
    @exists bit,
    @owner sysname,
    @obj sysname,@fld sysname,
    @cmd nvarchar(1024),
    @true tinyint,@false tinyint,
    @db sysname,@id int,
    @n int,
    @cond nchar(1)

select
    @true=1,@false=0,
    @cond='',''

if @objects is null goto ret
if charindex('','',@objects)=0 set @cond=''|''  -- OR condition

set @n=dbo.fn__str_count(@objects,@cond)
while (@n>0 and
        (@exists is null
        or (not (@exists=0 and @cond='','')   -- exists and exists...
            and not (@exists=1 and @cond=''|'')   -- exists or exists
            )
        )
    )
    begin
    -- dbo.fn__sql_unquotename not possibile because can be [dbo].[...]

    select  @obj=dbo.fn__str_at(@objects,@cond,@n),
            @fld=null,@id=null,
            @n=@n-1

    -- special case for field
    if @type=''fl'' select @fld=parsename(@obj,1),@obj=parsename(@obj,2)

    -- special case for temp
    if left(@obj,1)=''#'' or left(@obj,2)=''[#''
        select @obj=''tempdb..''+@obj

    select @id=object_id(@obj)
    if @type=''fk'' -- foreign keys existance
        begin
        if exists(
                select null from dbo.sysforeignkeys fk with (nolock)
                where fkeyid=@id
                )
            set @exists=@true
        else
            select @exists=@false
        end
    else
        begin
        if not @id is null
            begin
            select @exists=@true
            if not @fld is null
            and not exists(
                select top 1 null
                from syscolumns
                where id=@id and [name]=@fld
                ) select @exists=@false
            end -- obj.fld
        else
            select @exists=@false
        end -- normal obj
    end -- while

ret:
-- print @exists
return @exists
end -- fn__exists'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__exists:

-- =================================================================== fn__flags
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__flags',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120403
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__flags') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__flags') with nowait
        goto skip_fn__flags
        end
    if @ver>120403
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__flags') with nowait
        goto skip_fn__flags
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__flags') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__flags'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__flags]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:obj,utility
    v:120403\s.zaglio: added to group utility
    v:120125\s.zaglio: return null when unk
    v:110323\s.zaglio: convert mnemonic flags to numeric
    t:select dbo.fn__flags(''ABCD''),dbo.fn__flags(''E'')
    t:
        select
            dbo.fn__flags(''P''),dbo.fn__hex(dbo.fn__flags(''P'')),
            dbo.fn__flags(''OP''),dbo.fn__hex(dbo.fn__flags(''OP'')),
            dbo.fn__flags(''AP''),dbo.fn__hex(dbo.fn__flags(''AP''))
    t:select dbo.fn__flags(''q'') -- error
    t:declare @s smallint select @s=32768 select 0xffff-@s
*/
CREATE function fn__flags(@flags nvarchar(16))
returns smallint
as
begin
declare @ret smallint,@i int,@l int
if isnumeric(@flags)=1 return convert(smallint,@flags)
select @ret=0,@i=1,@l=len(@flags)
while (@i<=@l)
    begin
    select @ret=
        case substring(@flags,@i,1)
        when ''a'' then @ret|1        -- 1
        when ''b'' then @ret|2        -- 2
        when ''c'' then @ret|4        -- 3
        when ''d'' then @ret|8        -- 4
        when ''e'' then @ret|16
        when ''f'' then @ret|32
        when ''g'' then @ret|64
        when ''h'' then @ret|128      -- 8
        when ''i'' then @ret|256
        when ''j'' then @ret|512
        when ''k'' then @ret|1024
        when ''l'' then @ret|2048
        when ''m'' then @ret|4096
        when ''n'' then @ret|8192
        when ''o'' then @ret|16384
        when ''p'' then ~@ret        -- 16
        else null
        end
    select @i=@i+1
    end
return @ret
end -- fn__flags'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__flags:

-- ================================================================= fn__flags32
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__flags32',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120404
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__flags32') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__flags32') with nowait
        goto skip_fn__flags32
        end
    if @ver>120404
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__flags32') with nowait
        goto skip_fn__flags32
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__flags32') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__flags32'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__flags32]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:obj,utility
    v:120404\s.zaglio: convert a,b,c,... into power of 2
    t:select dbo.fn__flags32(''A''),dbo.fn__flags32(''AB''),dbo.fn__flags32(''Z'')
    t:select cast(dbo.fn__flags32(''AZ'') as binary(4)),dbo.fn__flags32(''AZ'')
*/
CREATE function fn__flags32(@flags nvarchar(32))
returns int
as
begin
declare @ret int,@i int,@l int,@asc int, @asc_a int,@asc_z int
if isnumeric(@flags)=1 return convert(int,@flags)
select
    @ret=0,@i=1,
    @l=len(@flags),
    @asc_a=ascii(''A''),
    @asc_z=ascii(''Z'')-@asc_a,
    @flags=upper(@flags)

while (@i<=@l)
    begin
    select @asc=ascii(substring(@flags,@i,1))-@asc_a
    if @asc=@asc_z select @ret=~@ret
    else select @ret=@ret|power(2,@asc)
    select @i=@i+1
    end
return @ret
end -- fn__flags32'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__flags32:

-- ============================================================ fn__flds_convert
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__flds_convert',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131014
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__flds_convert') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__flds_convert') with nowait
        goto skip_fn__flds_convert
        end
    if @ver>131014
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__flds_convert') with nowait
        goto skip_fn__flds_convert
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__flds_convert') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__flds_convert'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__flds_convert]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131014\s.zaglio: d tag adjust
    v:100228\s.zaglio: standard types convertion for tables
    d:100228\s.zaglio: fn__flds_row
    t:
        create table test_convert(a int,b float,c datetime,t sysname)
        select * into #test_convert  from test_convert
        select * into ##test_convert from test_convert
        print ''table:''+dbo.fn__flds_convert(''test_convert'',null,''t'')
        print ''#table:''+dbo.fn__flds_convert(''#test_convert'',null,null)
        print ''##table:''+dbo.fn__flds_convert(''##test_convert'',null,null)
        print isnull(dbo.fn__flds_convert(null,null,null),''???'')
        drop table test_convert
        drop table #test_convert
        drop table ##test_convert
*/
CREATE function [dbo].[fn__flds_convert](
    @tbl sysname,
    @seps nvarchar(32)='','',
    @excludes sysname=''''
)
returns nvarchar(4000)
as
begin
declare @convs nvarchar(4000),@db sysname,@obj sysname,@i int
declare @ex table([name] sysname)
if @seps is null select @seps='',''
-- print parsename(''[db..obj]'',1)
-- print parsename(''db..[obj]'',1)
select @db=parsename(@tbl,3)
select @obj=parsename(@tbl,1)
if left(@obj,1)=''#'' select @tbl=''tempdb..''+@obj

if not @excludes is null
    insert @ex select token
    from dbo.fn__str_table(@excludes,@seps)

if left(@obj,1)=''#''
    select
            @convs=coalesce( @convs + @seps, '''') +
                case when t.name in (''datetime'', ''smalldatetime'')
                     then ''convert(nvarchar(4000),''+quotename(c.name)+'',126) as '' + quotename(c.name)
                     when t.name in (''numeric'', ''decimal'')
                     then ''convert(nvarchar(4000),''+quotename(c.name)+'',128) as '' + quotename(c.name)
                     when t.name in (''float'', ''real'', ''money'', ''smallmoney'')
                     then ''convert(nvarchar(4000),''+quotename(c.name)+'',2) as '' + quotename(c.name)
                     when t.name in (''datetime'', ''smalldatetime'')
                     then ''convert(nvarchar(4000),''+quotename(c.name)+'',120) as '' + quotename(c.name)
                     when t.name in (''image'',''text'',''ntext'')
                     then ''dbo.fn__hex(textptr(''+quotename(c.name)+'')) as '' + quotename(c.name)
                     else quotename(c.name)
                end
    from tempdb..syscolumns c
    join tempdb..systypes t on c.xusertype=t.xusertype
    where c.[id]=object_id(@tbl)
    and not c.name in (select [name] from @ex)
    order by colorder
else
    select
            @convs=coalesce( @convs + @seps, '''')+
                case when t.name in (''datetime'', ''smalldatetime'')
                     then ''convert(nvarchar(4000),''+quotename(c.name)+'',126) as '' + quotename(c.name)
                     when t.name in (''numeric'', ''decimal'')
                     then ''convert(nvarchar(4000),''+quotename(c.name)+'',128) as '' + quotename(c.name)
                     when t.name in (''float'', ''real'', ''money'', ''smallmoney'')
                     then ''convert(nvarchar(4000),''+quotename(c.name)+'',2) as '' + quotename(c.name)
                     when t.name in (''datetime'', ''smalldatetime'')
                     then ''convert(nvarchar(4000),''+quotename(c.name)+'',120) as '' + quotename(c.name)
                     when t.name in (''image'',''text'',''ntext'')
                     then ''dbo.fn__hex(textptr(''+quotename(c.name)+'')) as '' + quotename(c.name)
                     else quotename(c.name)
                end
    from syscolumns c
    join systypes t on c.xusertype=t.xusertype
    where c.[id]=object_id(@obj)
    and not c.name in (select [name] from @ex)
    order by colorder

return @convs
end -- fn__flds_convert'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__flds_convert:

-- ================================================================= fn__flds_of
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__flds_of',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130730
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__flds_of') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__flds_of') with nowait
        goto skip_fn__flds_of
        end
    if @ver>130730
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__flds_of') with nowait
        goto skip_fn__flds_of
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__flds_of') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__flds_of'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__flds_of]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130730\s.zaglio: adapted to mssql2k5 and replaced prev ordby with @tcol pkey
    v:130529\l.mazzanti: trovato un caso in cui si perdeva l''orderby del colorder
    v:121011\s.zaglio: added like on exclusions instead of =
    v:120413,100710\s.zaglio: add.%id% to exclude identity;order by number for table functions
    v:100228\s.zaglio: a remake more simple & faster (as fn__flds_convert)
    v:090915\S.Zaglio: added #temp table management
    v:081224,081121\S.Zaglio: manage quoted name;now @excludes need same @seps
    v:081110,081012\S.Zaglio: expanded @seps to nvarchar(32);expanded @seps to nvarchar(8)
    v:081009\S.Zaglio: added quoting for fields with spaces inside
    v:080721\S.Zaglio: return list of flields separated by comma with optional exlusions of flds separated by |
    t:sp__flds_of_test
*/
CREATE function [dbo].[fn__flds_of](
    @tbl sysname,
    @seps nvarchar(32)='','',
    @excludes sysname=''''
    )
returns nvarchar(4000)
as
begin

declare
    @cols nvarchar(4000),@db sysname,@obj sysname,@i int,
    @sql nvarchar(4000),@oid int,@tmp bit,@ex_ident bit,
    @nexcludes int

declare @ex table([name] sysname primary key)
declare @tcols table([name] sysname, colorder int primary key)

if @seps is null select @seps='',''
-- print parsename(''[db..obj]'',1)
-- print parsename(''db..[obj]'',1)

select
    @db=parsename(@tbl,3),
    @obj=parsename(@tbl,1),
    @tmp=case when left(@obj,1)=''#'' then 1 else 0 end,
    @ex_ident=charindex(''%id%'',@excludes)

if (not @db is null and @db!=db_name()) return null

if @tmp=1 select @tbl=''tempdb..''+@obj
select @oid=object_id(@tbl)
if @oid is null return null

/*  this fn is so slower that is better
    delete excludes to the end instead of while */

if not @excludes is null
    begin
    insert @ex select token
    from dbo.fn__str_table(@excludes,@seps)
    where token!=''%id%''
    select @nexcludes=@@rowcount
    end

-- collect cols
if @ex_ident=1
    begin
    if @tmp=1
        insert @tcols
        select c.name, c.column_id
        -- select *
        from tempdb.sys.columns c
        where c.[object_id]=@oid
        and c.is_identity=0
        order by column_id
    else
        insert @tcols
        select c.name, c.column_id
        from sys.columns c
        where c.[object_id]=@oid
        and is_identity=0
        order by column_id
    end
else    -- with identity
    begin
    if @tmp=1
        insert @tcols
        select c.name, c.column_id
        from tempdb.sys.columns c
        where c.[object_id]=@oid
        order by c.column_id
    else
        insert @tcols
        select c.name, c.column_id
        from sys.columns c
        where c.[object_id]=@oid
        order by column_id
    end

if @nexcludes>0
    delete a from @tcols a join @ex b on a.[name] like b.[name]

select @cols=coalesce(@cols+@seps,'''')+c.name from @tcols c

return @cols
end -- fn__flds_of'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__flds_of:

-- ========================================================== fn__flds_quotename
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__flds_quotename',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__flds_quotename') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__flds_quotename') with nowait
        goto skip_fn__flds_quotename
        end
    if @ver>100919
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__flds_quotename') with nowait
        goto skip_fn__flds_quotename
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__flds_quotename') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__flds_quotename'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__flds_quotename]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100919\s.zaglio: quote numeric fields names
    v:100405\s.zaglio: re???added reserved keywords
    v:091128\s.zaglio: added use of fn__token_sql
    v:090916\S.Zaglio: added some keywords
    v:090627\S.Zaglio: added keywords as,where
    v:090304\S.Zaglio: add bounds [ & ] if have space or chars or is a keyword
    t:print dbo.fn__flds_quotename(''test,te/st,test test,test_test,group,set,[already_quoted]'','','')
*/
CREATE  function [dbo].[fn__flds_quotename](
    @flds nvarchar(4000),
    @seps nvarchar(32)='',''
)
returns nvarchar(4000)
as
begin
declare @r nvarchar(4000),@name sysname,@i int,@n int
declare @t table (token sysname)
insert @t select token from dbo.fn__str_table(@flds,@seps)
update @t set token=quotename(token)
where left(token,1)!=''[''
and (patindex(''%[^0-9A-Za-z,_]%'', token)>0
     or dbo.fn__token_sql(token)=1
     or isnumeric(token)=1
    )
select @r=coalesce(@r+@seps,'''')+token from @t
/*
set @r='''' set @i=1 set @n=dbo.fn__str_count(@flds,@seps)
while (@i<=@n)
    begin
    set @name=dbo.fn__str_at(@flds,@seps,@i)
    if left(@name,1)<>''['' and right(@name,1)<>'']''
        begin
        if charindex('' '',@name)>0 or dbo.fn__token_sql(@name)=1
            set @name=quotename(@name)
        end
    if @i>1 set @r=@r+@seps
    set @r=@r+@name
    set @i=@i+1
    end -- while
*/
return @r
end -- fn__flds_quotename'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__flds_quotename:

-- =========================================================== fn__flds_row_size
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__flds_row_size',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100501
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__flds_row_size') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__flds_row_size') with nowait
        goto skip_fn__flds_row_size
        end
    if @ver>100501
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__flds_row_size') with nowait
        goto skip_fn__flds_row_size
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__flds_row_size') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__flds_row_size'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__flds_row_size]

begin try
exec dbo.sp_executesql @statement = N'/*    leave this
    l:see LICENSE file
    g:utility
    v:100501\s.zaglio: added @extra (for each columns) to calc nulls
    v:100228\s.zaglio: a correction on description (in bytes)
    v:091018\s.zaglio: return row size (in bytes) of fields of a table
    t:
        print dbo.fn__flds_row_size(''sysobjects'','','',''name,id,xtype'',default)
        select * from syscolumns where id=object_id(''sysobjects'') order by colid
    t:
        create table #test(a varchar(4),b nvarchar(4), c bit, d int) -- 4+8+?+4=16+?
        print dbo.fn__flds_row_size(''#test'','','',''a,b,c,d'',default)
        print dbo.fn__flds_row_size(''#test'','','',''a,b,c,d'',1)
        drop table #test
    t:sp__find ''fn__flds_row_size''
*/
CREATE function [dbo].[fn__flds_row_size](@tbl sysname,@sep nvarchar(32),@flds nvarchar(4000),@extra real=0)
returns int
as
begin
declare @r int
if @sep is null select @sep='',''
select @flds=@sep+@flds+@sep
if left(@tbl,1)=''#''
    begin
    select @tbl=''tempdb..''+@tbl
    select @r=sum(length)+@extra*count(*) from tempdb..syscolumns
    where id=object_id(@tbl) and charindex(@sep+[name]+@sep,@flds)>0
    end
else
    select @r=sum(length)+@extra*count(*) from syscolumns
    where id=object_id(@tbl) and charindex(@sep+[name]+@sep,@flds)>0
return @r
end -- func'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__flds_row_size:

-- ============================================================ fn__flds_size_of
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__flds_size_of',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090916
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__flds_size_of') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__flds_size_of') with nowait
        goto skip_fn__flds_size_of
        end
    if @ver>090916
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__flds_size_of') with nowait
        goto skip_fn__flds_size_of
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__flds_size_of') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__flds_size_of'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__flds_size_of]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090916\S.Zaglio: a better managemnt of temp tables
    v:090202\S.Zaglio: return a series of field''s sizes of table
    t: print dbo.fn__flds_size_of(''sysobjects'',''|'',null) --> name and id and xtype ...
*/
CREATE function [dbo].[fn__flds_size_of](@tbl sysname, @seps nvarchar(32)='','',@excludes sysname='''')
returns nvarchar(4000)
as
begin
set @tbl=dbo.fn__sql_unquotename(@tbl)
if @excludes<>'''' set @excludes=@excludes+@seps -- coz error into fn_str_at

declare @flds nvarchar(4000) set @flds=''''

declare @tmp bit
if left(@tbl,1)=''#'' set @tbl=''tempdb..''+@tbl
if charindex(''.#'',@tbl)>0 set @tmp=1 else set @tmp=0

if @tmp=0
    declare flds cursor local for
        select convert(nvarchar(10),t.length) as length from syscolumns c
        inner join systypes t on c.xusertype=t.xusertype
        where c.[id]=object_id(@tbl) order by colorder
else
    declare flds cursor local for
        select convert(nvarchar(10),t.length) as length from tempdb..syscolumns c
        inner join tempdb..systypes t on c.xusertype=t.xusertype
        where c.[id]=object_id(@tbl) order by colorder

declare @fld sysname
declare @i int
declare @n int set @n=dbo.fn__str_count(@excludes,default)
declare @no bit

open flds
while (1=1) begin
    fetch next from flds into @fld
    if @@error != 0 or @@fetch_status != 0 BREAK

    set @no=0
    if @excludes<>'''' begin
        set @i=1
        while (@i<=@n) begin
            if dbo.fn__at(@fld,@excludes,@seps)<>0 begin set @no=1 break end
            set @i=@i+1
        end -- while
    end -- if @excludes
    if @no=0 begin
        if charindex('' '',@fld)>0 set @fld=quotename(@fld)
        if @flds<>'''' set @flds=@flds+@seps
        set @flds=@flds+@fld
    end -- @no
end -- while cursor

close flds
deallocate flds
return @flds
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__flds_size_of:

-- ============================================================ fn__flds_type_of
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__flds_type_of',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=151106
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__flds_type_of') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__flds_type_of') with nowait
        goto skip_fn__flds_type_of
        end
    if @ver>151106
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__flds_type_of') with nowait
        goto skip_fn__flds_type_of
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__flds_type_of') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__flds_type_of'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__flds_type_of]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:151106\s.zaglio: added collate database_default
    v:111205\s.zaglio: added ?varchar(max)
    v:100710\s.zaglio: orderred by number
    v:100228\s.zaglio: a remake more simple & faster (as fn__flds_convert)
    v:091018\s.zaglio: a bug on double size of nvarchar
    v:090916\S.Zaglio: a better managemnt of temp tables
    v:090331\S.Zaglio: added manage of ## tables
    v:090202\S.Zaglio: return a series of field''s types of table
    t: print dbo.fn__flds_type_of(''cfg'',''|'',null) --> name and id and xtype ...
    t:
        create table tst_fn_flds(
            i int, r real, v varchar(10), nv nvarchar(10),
            s sysname, n numeric(2,1), vcmax nvarchar(max),
            ex int)
        select * into #tst_fn_flds from tst_fn_flds
        print dbo.fn__flds_type_of(''tst_fn_flds'','','',''ex'')
        print dbo.fn__flds_type_of(''#tst_fn_flds'','','',null)
        drop table #tst_fn_flds drop table tst_fn_flds
*/
CREATE function [dbo].[fn__flds_type_of](@tbl sysname, @seps nvarchar(32)='','',@excludes sysname='''')
returns nvarchar(4000)
as
begin
declare @cols nvarchar(4000),@db sysname,@obj sysname,@i int
declare @ex table([name] sysname)
if @seps is null select @seps='',''
-- print parsename(''[db..obj]'',1)
-- print parsename(''db..[obj]'',1)
select @db=parsename(@tbl,3)
select @obj=parsename(@tbl,1)
if left(@obj,1)=''#'' collate database_default select @tbl=''tempdb..''+@obj

if not @excludes is null
    insert @ex select token
    from dbo.fn__str_table(@excludes,@seps)

if left(@obj,1)=''#'' collate database_default
    select @cols
        =coalesce( @cols+ @seps, '''') + t.name
        +case -- select * from systypes
         when t.xusertype in (175,239,231,167) --char,nchar,nvc,vc
            then ''('' +
                    case
                    when c.prec=-1
                    then ''max''
                    else cast(c.prec as nvarchar)
                    end +
                 '')''
                                   -- (@len/case
                                   --when left(@column,1) =''n''
                                   --then 2 else 1 end) as nvarchar) + '') ''
            when t.xusertype in (106,108) then ''('' + cast(c.prec as nvarchar)+'',''+
                                                     cast(c.scale as nvarchar)+'') ''
         else '''' end
    from tempdb..syscolumns c
    join tempdb..systypes t on c.xusertype=t.xusertype
    where c.[id]=object_id(@tbl)
    and not c.name in (select [name] collate database_default from @ex)
    order by c.number,c.colorder
else
    select @cols
        =coalesce( @cols+ @seps, '''') + t.name
        +case -- select * from systypes
         when t.xusertype in (175,239,231,167) --char,nchar,nvc,vc
            then ''('' +
                    case
                    when c.prec=-1
                    then ''max''
                    else cast(c.prec as nvarchar)
                    end +
                 '')''
                                   -- (@len/case
                                   --when left(@column,1) =''n''
                                   --then 2 else 1 end) as nvarchar) + '') ''
            when t.xusertype in (106,108) then ''('' + cast(c.prec as nvarchar)+'',''+
                                                     cast(c.scale as nvarchar)+'') ''
         else '''' end
    from syscolumns c
    join systypes t on c.xusertype=t.xusertype
    where c.[id]=object_id(@tbl)
    and not c.name in (select [name] collate database_default from @ex)
    order by c.number,c.colorder

return @cols
end -- fn__flds_type_of'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__flds_type_of:

-- ================================================================== fn__format
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__format',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130510
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__format') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__format') with nowait
        goto skip_fn__format
        end
    if @ver>130510
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__format') with nowait
        goto skip_fn__format
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__format') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__format'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__format]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,io
    v:130510\s.zaglio: added 0<n
    v:130329\s.zaglio: added $<, comments to disambiguate (search 130329)
    v:120523\s.zaglio: a bug near use of goto, when format is a date but @val a string
    v:120406\s.zaglio: added @ tag for string format
    v:110805\s.zaglio: added more symbols to ^ format
    v:110804\s.zaglio: added management of bad float convertion for 0<
    v:110706\s.zaglio: added [eng] format
    v:110705\s.zaglio: a bug near 0<
    v:110620\s.zaglio: expanded left pad format
    v:110601\s.zaglio: added ^ fmt
    v:110527\s.zaglio: added chk of type of @val and name normalization
    v:110516\s.zaglio: used by importing proc
    t:sp__format
                                                                   #
    ###########################################################   ##
    ## -- PLEASE --                                          ##  #######
    ## see/update "sp__format" and read comment''s'' of 130329 ## ########
    ##                                                       ##  #######
    ###########################################################   ##
                                                                   #
*/
CREATE function fn__format(
    @val sql_variant,
    @fmt nvarchar(128),
    @len int
    )
returns nvarchar(4000)
as
begin

declare
    @ret nvarchar(4000),@svp varchar(32),@i int,
    @tmp nvarchar(4000),@l int,@c nvarchar(2),
    @lc int,@lret int,@p int

select @svp=convert(varchar(32),sql_variant_property(@val,''basetype''))

if @svp=''datetime''          goto date_format
if left(@fmt,1)=''@''         goto string_format
if left(@fmt,2)=''0<''        goto string_0left_padding
if @fmt=''$<''                goto money_0left_padding
if substring(@fmt,2,1)=''<''  goto specific_pad
if @fmt=''[eng]''             goto verbalize
if @fmt=''^''                 goto replace_accents
if @fmt in (
    ''AN'',   -- alfanumeric:replace symbols with _ (only 0-9a-Z)
    ''ANs''   -- 120315\alfanumeric:replace symbols with one _ (only 0-9a-Z)
    )                       goto alphanumeric
if @len is null
    select @ret=convert(nvarchar(4000),@val)
else
    select @ret=left(convert(nvarchar(4000),@val),@len)
goto ret

-- ============================================================== date_format ==

date_format:
if @fmt=''YYYY''                          -- 110722\s.zaglio
    begin
    select @ret=convert(datetime,@val,8)
    select @ret=cast(year(convert(datetime,@val,8)) as nvarchar(4))
    goto ret
    end -- yyyy
if @fmt=''HHMM''
    begin
    select @ret=convert(nvarchar(32),@val,8)
    select @ret=substring(@ret,1,2)+substring(@ret,4,2)
    goto ret
    end -- hhmm
if @fmt=''YYYYMMDDHHMMSS''
    begin
    select @ret=convert(nvarchar(32),@val,8)
    select @ret=convert(nvarchar(32),@val,112)
               +substring(@ret,1,2)+substring(@ret,4,2)
               +substring(@ret,7,2)
    goto ret
    end -- YYYYMMDDHHMMSS
if @fmt=''YYMMDD_HHMM''                   -- 111115\s.zaglio
    begin
    select @ret=convert(nvarchar(32),@val,8)
    select @ret=convert(nvarchar(32),@val,12)
               +''_''
               +substring(@ret,1,2)+substring(@ret,4,2)
    goto ret
    end -- YYMMDDHHMM
if @fmt=''YYYYMMDD_HHMMSS''               -- 110615\s.zaglio
    begin
    select @ret=convert(nvarchar(32),@val,8)
    select @ret=convert(nvarchar(32),@val,112)
               +''_''
               +substring(@ret,1,2)+substring(@ret,4,2)
               +substring(@ret,7,2)
    goto ret
    end -- YYYYMMDDHHMMSS
if @fmt=''YYYYMMDD_HHMM''
    begin
    select @ret=convert(nvarchar(32),@val,8)
    select @ret=convert(nvarchar(32),@val,112)
               +''_''
               +substring(@ret,1,2)+substring(@ret,4,2)
    goto ret
    end -- YYYYMMDD_HHMM
if @fmt=''HHMMSS''                        -- 120109\s.zaglio
    begin
    select @ret=convert(nvarchar(32),@val,8)
    select @ret=substring(@ret,1,2)+substring(@ret,4,2)
               +substring(@ret,7,2)
    goto ret
    end -- HHMMSS

if @fmt=''DD/MM/YYYY HH:MM:SS''           -- 120821\s.zaglio
    begin
    select @ret=convert(nvarchar(32),@val,103)
               +'' ''
               +convert(nvarchar(32),@val,108)
    goto ret
    end -- DD/MM/YYYY HH:MM:SS

/* 130329\s.zaglio:
    fn__format covert format not already coveder by TSQL CONVERT;
    CONVERT formats have been left out (sono stati lasciati fuori)
    because call of fn__format is extremely slower that CONVERT
    and REPLACE+CONVERT.
    I left this to not break code.
*/

if @fmt=''DDMMYYYY''                      -- 121112\A.Sirna
    begin
    select @ret=replace(convert(nvarchar(32),@val,103),''/'','''')
    goto ret
    end -- DDMMYYYY

if @fmt=''YYYYMMDD''                      -- 121112\A.Sirna
    begin
    select @ret=convert(nvarchar(32),@val,112)
    goto ret
    end -- YYYYMMDD

goto ret

-- ====================================================== string left padding ==
string_0left_padding:
/* 130329\s.zaglio:
    bigint was choosen instead of money because money has a fixed 2 digit and
    uses the ''decimal separator'' of the local lang.
    A 0 left padded string is necessary to manage a variable number of digits,
    without use the variable and ambigous decimal separator;
    of course an according between sender and receiver or writer and reader must
    pre-exists
*/
-- select dbo.fn__format(123.435,''0<2'',10)
-- select round(123.435,2)

if len(@fmt)>2
    begin
    select @i=cast(substring(@fmt,3,4) as int)
    select @ret=convert(nvarchar(4000),
                        round(cast(@val as float),@i)*power(10,@i)
                       )
    end
else
    select @ret=convert(nvarchar(4000),cast(@val as bigint))
select @ret=replicate(''0'',@len-len(@ret))+@ret
goto ret

money_0left_padding:
select @ret=convert(nvarchar(4000),cast(@val as money))
select @ret=replicate(''0'',@len-len(@ret))+@ret
goto ret

-- =================================================== specific char left pad ==
specific_pad:
-- ##########################
-- ##
-- ## be careful with len('' '')=0 while datalength('' '')=1
-- ##
-- ########################################################

-- select dbo.fn__format(''fn__format'',''=< '',80)
-- todo: expand formats to: 0<.0, 0<.00, 0<.000 and so on
-- todo: expand formats to: 0<,0, 0<,00, 0<,000 and so on
-- todo: expand formats to: 0<00, 0<000, to multiply x XXX and remove decimal sign
select @ret=convert(nvarchar(4000),@val),
       @c=substring(@fmt,3,1),
       @lc=datalength(@c),@lret=len(@ret)

if @lc=0
    select @ret=replicate(
                    substring(@fmt,1,1),
                    @len-@lret
                    )+@ret
else
    select @ret=replicate(
                    substring(@fmt,1,1),
                    @len-(@lret+
                        case when @c='' ''
                             then 1
                             else @lc
                        end)
                    )+@c+@ret
goto ret

-- =================================================== normalize anphanumeric ==

alphanumeric:
select @ret=convert(nvarchar(4000),@val)
select @l=len(@ret)
while @l>0
    if not substring(@ret,@l,1) like ''[a-zA-Z0-9]''
        select @ret=stuff(@ret,@l,1,''_''),@l=@l-1
    else
        select @l=@l-1
if @fmt=''ANs''
    begin
    while charindex(''__'',@ret)>0 select @ret=replace(@ret,''__'',''_'')
    select @i=1,@l=len(@ret)
    while substring(@ret,@i,1)=''_'' select @i=@i+1
    while substring(@ret,@l,1)=''_'' select @l=@l-1
    select @ret=substring(@ret,@i,@l-@i+1)
    end
goto ret

-- ========================================================= replaces accents ==

replace_accents:
-- select dbo.fn__format(''a.é.É.t.è.ù.õ.Ö.Ì.ï.ŝ.s'',''^'',default)
/*  declare @c nvarchar(2),@val sql_variant,@tmp
    sysname,@ret sysname,@l int,@i int select @val=''é.É.t.è.ù.õ.Ö.Ì.ï''
*/
declare
    @cc varbinary(8), @acc varbinary(256),@accd varbinary(256),
    @rep nvarchar(128),@repd nvarchar(128),@w int
/*  ÂÃÄÀÁÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĒřŠŝş
    öèéòìàù ĀāĒóôåæ ÀÁÆËÏÍÌ çćĉřŠŝş''
    ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöùúûüýþÿĀāĂăĄąĆćĈĉĊċČč
    ĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıĲĳĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇňŉŊŋŌōŎŏŐőŒœ
    ŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƏƒƠơƯưǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǺǻǼǽǾǿØ
*/
select
    @w=len(cast(N'' '' as varbinary(8))),
    @tmp=convert(nvarchar(4000),@val),
    @ret='''',@i=1,@l=len(@tmp),

    /*  method:
        Research done by position:
             4 to the position of @ acc (Ç) is the rep of @ 4 (C)
    */
    -- @acc contains accented characters to be replaced by individual
    --                    1         2         3         4         5
    --          012345678901234567890123456789012345678901234567890
    @acc=cast(N''ÂÃÅÇÊËÎÐÑÔÕØÛÝÞßâãåçêëîðñôõøûýþĀāĒřŠŝş°¦ª'' as varbinary(256)),
    @rep=     N''AAACEEIDNOOOUYBBaaaceeionooouybAaErSss.|a'',

    -- @accd contains accented characters to be replaced by double
    --                     1         2         3
    --           0123456789012345678901234567890
    @accd=cast(N''ÄÀÁÆÈÉÌÍÏÒÓÖÙÚÜàáäæèéìíïòóöùúü'' as varbinary(256)),
    --                                      1                         2                          3
    --           0 1  2  3 4  5  6  7  8 9  0  1 2  3  4 5  6  7 8 9  0  1  2  3 4  5  6 7  8  9 0
    @repd=     N''AEA''''A''''AEE''''E''''I''''I''''IIO''''O''''OEU''''U''''UEa''''a''''aeaee''''e''''i''''i''''iio''''o''''oeu''''u''''ue''

if (@tmp like ''%[''+cast(@acc as nvarchar(128))+cast(@accd as nvarchar(128))+'']%'' )

    while @i<=@l
        begin
        select
            @c=substring(@tmp,@i,1),
            @cc=cast(@c as varbinary(4)),
            @i=@i+1
        -- exec sp__printf ''cc=%s'',@cc
        select @p=charindex(@cc,@acc)
        if @p=0
            begin
            select @p=charindex(@cc,@accd)
            if @p>0 select @c=substring(@repd,@p,2)
            end
        else
            select @c=substring(@rep,@p/@w+1,1)
        select @ret=@ret+@c
        -- exec sp__printf ''c=%s; p=%d'',@c,@p
        end -- while
else
    select @ret=@tmp
-- print @ret
goto ret

-- ================================================================ verbalize ==
verbalize:
/*  originally from:
    http://www.novicksoftware.com/udfofweek/Vol2/T-SQL-UDF-Vol-2-Num-9-udf_Num_ToWords.htm
*/
declare @inputnumber varchar(38)
declare @numberstable table (number char(2), word varchar(10))
declare @length int
declare @counter int
declare @loops int
declare @position int
declare @chunk char(3) -- for chunks of 3 numbers
declare @tensones char(2)
declare @hundreds char(1)
declare @tens char(1)
declare @ones char(1)
declare @number numeric (38, 0) -- input number with as many as 18 digits

select @number=convert(numeric(38,0),@val)
if @number = 0 return ''zero''

-- initialize the variables
select @inputnumber = convert(varchar(38), @number)
     , @ret = ''''
     , @counter = 1
select @length   = len(@inputnumber)
     , @position = len(@inputnumber) - 2
     , @loops    = len(@inputnumber)/3

-- make sure there is an extra loop added for the remaining numbers
if len(@inputnumber) % 3 <> 0 set @loops = @loops + 1

-- insert data for the numbers and words
insert into @numberstable   select ''00'', ''''
    union all select ''01'', ''one''      union all select ''02'', ''two''
    union all select ''03'', ''three''    union all select ''04'', ''four''
    union all select ''05'', ''five''     union all select ''06'', ''six''
    union all select ''07'', ''seven''    union all select ''08'', ''eight''
    union all select ''09'', ''nine''     union all select ''10'', ''ten''
    union all select ''11'', ''eleven''   union all select ''12'', ''twelve''
    union all select ''13'', ''thirteen'' union all select ''14'', ''fourteen''
    union all select ''15'', ''fifteen''  union all select ''16'', ''sixteen''
    union all select ''17'', ''seventeen'' union all select ''18'', ''eighteen''
    union all select ''19'', ''nineteen'' union all select ''20'', ''twenty''
    union all select ''30'', ''thirty''   union all select ''40'', ''forty''
    union all select ''50'', ''fifty''    union all select ''60'', ''sixty''
    union all select ''70'', ''seventy''  union all select ''80'', ''eighty''
    union all select ''90'', ''ninety''

while @counter <= @loops begin

    -- get chunks of 3 numbers at a time, padded with leading zeros
    set @chunk = right(''000'' + substring(@inputnumber, @position, 3), 3)

    if @chunk <> ''000'' begin
        select @tensones = substring(@chunk, 2, 2)
             , @hundreds = substring(@chunk, 1, 1)
             , @tens = substring(@chunk, 2, 1)
             , @ones = substring(@chunk, 3, 1)

        -- if twenty or less, use the word directly from @numberstable
        if convert(int, @tensones) <= 20 or @ones=''0'' begin
            set @ret = (select word
                                      from @numberstable
                                      where @tensones = number)
                   + case @counter when 1 then '''' -- no name
                       when 2 then '' thousand '' when 3 then '' million ''
                       when 4 then '' billion ''  when 5 then '' trillion ''
                       when 6 then '' quadrillion '' when 7 then '' quintillion ''
                       when 8 then '' sextillion ''  when 9 then '' septillion ''
                       when 10 then '' octillion ''  when 11 then '' nonillion ''
                       when 12 then '' decillion ''  when 13 then '' undecillion ''
                       else '''' end
                               + @ret
            end
         else begin -- break down the ones and the tens separately

             set @ret = '' ''
                            + (select word
                                    from @numberstable
                                    where @tens + ''0'' = number)
                             + ''-''
                             + (select word
                                    from @numberstable
                                    where ''0''+ @ones = number)
                   + case @counter when 1 then '''' -- no name
                       when 2 then '' thousand '' when 3 then '' million ''
                       when 4 then '' billion ''  when 5 then '' trillion ''
                       when 6 then '' quadrillion '' when 7 then '' quintillion ''
                       when 8 then '' sextillion ''  when 9 then '' septillion ''
                       when 10 then '' octillion ''  when 11 then '' nonillion ''
                       when 12 then '' decillion ''   when 13 then '' undecillion ''
                       else '''' end
                            + @ret
        end

        -- now get the hundreds
        if @hundreds <> ''0'' begin
            set @ret  = (select word
                                      from @numberstable
                                      where ''0'' + @hundreds = number)
                                + '' hundred ''
                                + @ret
        end
    end

    select @counter = @counter + 1
         , @position = @position - 3

end

-- remove any double spaces
set @ret = ltrim(rtrim(replace(@ret, ''  '', '' '')))
set @ret = upper(left(@ret, 1)) + substring(@ret, 2, 4000)
goto ret

-- ============================================================ string format ==

string_format:
declare @nc nchar,@mtpl nvarchar(64)
select
    @i=2,@l=len(@fmt),
    @mtpl=''1234567890abcdefghijklmnopqrsxyvwz'',
    @ret='''',@tmp=cast(@val as nvarchar(4000))

while (@i<=@l)
    select
        @nc=substring(@fmt,@i,1),
        @p=charindex(@nc,@mtpl),
        @ret=@ret+case @p when 0 then @nc else substring(@tmp,@p,1) end,
        @i=@i+1

goto ret

-- if not managed type
select @ret=left(convert(nvarchar(4000),@val),@len)

ret:
return @ret
end -- fn__format'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__format:

-- ============================================================== fn__global_get
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__global_get',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080402
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__global_get') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__global_get') with nowait
        goto skip_fn__global_get
        end
    if @ver>080402
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__global_get') with nowait
        goto skip_fn__global_get
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__global_get') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__global_get'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__global_get]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080402\S.Zaglio: read from a registry (of MSSQLServer\MyGlobalVars branch) the value
*/
CREATE  function fn__global_get(@variable sysname)
returns nvarchar(4000)
as
begin
declare @value nvarchar(4000)
EXECUTE master..xp_regread ''HKEY_LOCAL_MACHINE'',''SOFTWARE\Microsoft\MSSQLServer\MSSQLServer\MyGlobalVars'',@variable,@value out
return @value
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__global_get:

-- ============================================================== fn__global_set
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__global_set',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080402
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__global_set') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__global_set') with nowait
        goto skip_fn__global_set
        end
    if @ver>080402
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__global_set') with nowait
        goto skip_fn__global_set
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__global_set') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__global_set'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__global_set]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080402\S.Zaglio: read/write from a registry (of MSSQLServer\MyGlobalVars branch) the value
    t:print dbo.fn__global_set(''test'',''test value'') print dbo.fn__global_get(''test'')
*/
CREATE  function [dbo].[fn__global_set](@variable sysname,@value nvarchar(4000))
returns nvarchar(4000)
as
begin
declare @var sysname,@key sysname
select  @key=''SOFTWARE\Microsoft\MSSQLServer\MSSQLServer'',
        @var=''fgv_''+@variable
EXECUTE master..xp_regwrite ''HKEY_LOCAL_MACHINE'',@key,@var,''REG_SZ'',@value
select @value=null
EXECUTE master..xp_regread ''HKEY_LOCAL_MACHINE'',@key,@var,@value out
return @value
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__global_set:

-- ============================================================== fn__global_var
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__global_var',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080402
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__global_var') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__global_var') with nowait
        goto skip_fn__global_var
        end
    if @ver>080402
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__global_var') with nowait
        goto skip_fn__global_var
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__global_var') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__global_var'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__global_var]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080402\S.Zaglio: read/write/delete a registry (of MSSQLServer\MyGlobalVars branch) with value
    t:print dbo.fn__global_var(''test'',''test value'') print dbo.fn__global_var(''test'',''test 1'') print dbo.fn__global_var(''test'',null)
    t:
        DECLARE @AuditLevel int

        EXEC master..xp_regread
          @rootkey=''HKEY_LOCAL_MACHINE'',
          @key=''SOFTWARE\Microsoft\MSSQLServer\MSSQLServer'',
          @value_name=''AuditLevel'',
          @value=@AuditLevel OUTPUT

        SELECT @AuditLevel
*/
CREATE  function [dbo].[fn__global_var](@variable sysname,@value nvarchar(4000))
returns nvarchar(4000)
as
begin
declare @var sysname,@key sysname
select  @key=''SOFTWARE\Microsoft\MSSQLServer\MSSQLServer'',
        @variable=''fgv_''+@variable
if @value is null begin
    EXECUTE master..xp_regdeletevalue ''HKEY_LOCAL_MACHINE'',@var,@variable
    return null
    end
declare @old_value nvarchar(4000)
EXECUTE master..xp_regread ''HKEY_LOCAL_MACHINE'',@key,@variable,@old_value out
if @old_value is null begin
    EXECUTE master..xp_regwrite ''HKEY_LOCAL_MACHINE'',@key,@variable,''REG_SZ'',@value
    EXECUTE master..xp_regread ''HKEY_LOCAL_MACHINE'',@key,@variable,@value out
    return @value
    end
return @old_value
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__global_var:

-- =========================================================== fn__group_version
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__group_version',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120802.1100
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__group_version') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__group_version') with nowait
        goto skip_fn__group_version
        end
    if @ver>120802.1100
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__group_version') with nowait
        goto skip_fn__group_version
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__group_version') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__group_version'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__group_version]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:script,version,group
    v:120802.1100\s.zaglio: return last version of a group
    t:select dbo.fn__group_version(''utility'') -- 4 secs reduced to 2
*/
CREATE function fn__group_version(@grp sysname)
returns sysname
as
begin
declare @t table(id int,tag char,val sysname,primary key(id,tag))
insert @t(id,tag,val)
select obj_id,tag,val1 from fn__script_info(default,''rvg'',0) a
where not tag is null

select @grp=max(a.val)
from @t a join @t b on a.id=b.id and a.tag!=''g'' and b.tag=''g''

return @grp
end -- fn__group_version'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__group_version:

-- ==================================================================== fn__hash
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__hash',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130707
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__hash') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__hash') with nowait
        goto skip_fn__hash
        end
    if @ver>130707
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__hash') with nowait
        goto skip_fn__hash
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__hash') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__hash'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__hash]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:hash,md5,sha1,hashbytes,limits,8000,chars,len
    v:130707\s.zaglio: bypasses the limit of 8000 bytes of MS hashbytes
    c:originally from fn_hashbytesMAX of Brandon Galderisi
    t:
        select dbo.fn__hash(cast(''test'' as varbinary(max)),''md5'')
        -- 0x8706F8479081122D32400F2E681D26C4
        select dbo.fn__hash(cast(N''test'' as varbinary(max)),''md5'')
        -- 0x2BC0BA5B64772F46C3876E043FBF6775
    t:select dbo.fn__script_sign(''fn__hash'',default)
*/
CREATE function dbo.fn__hash(
    @data varbinary(max),
    @algo varchar(10)
    )
returns varbinary(8000)
as
begin
declare
    @concat varchar(max),@hash varbinary(8000)

;with a as (select 1 as n union all select 1) -- 2
     ,b as (select 1 as n from a ,a a1)       -- 4
     ,c as (select 1 as n from b ,b b1)       -- 16
     ,d as (select 1 as n from c ,c c1)       -- 256
     ,e as (select 1 as n from d ,d d1)       -- 65,536
     ,f as (select 1 as n from e ,e e1)       -- 4,294,967,296=17+trillion chrs
     ,factored as (select row_number() over (order by n) rn from f)
     ,factors as (select rn,(rn*4000)+1 factor from factored)

select @concat = cast((
select right(sys.fn_varbintohexstr
             (
             hashbytes(@algo, substring(@data, factor - 4000, 4000))
             )
          , 40) + ''''
from factors
where rn <= ceiling(datalength(@data)/(4000.0))
for xml path('''')
) as nvarchar(max))

if len(@concat)>8000
    select @hash=dbo.fn__hash(cast(@concat as varbinary(max)),@algo)
else
    select @hash=hashbytes(@algo,@concat)

return @hash
end -- fn__hash'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__hash:

-- ===================================================================== fn__hex
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__hex',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130929
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__hex') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__hex') with nowait
        goto skip_fn__hex
        end
    if @ver>130929
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__hex') with nowait
        goto skip_fn__hex
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__hex') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__hex'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__hex]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    c:http://blogs.msdn.com/b/sqltips/archive/2008/07/02/converting-from-hex-string-to-varbinary-and-vice-versa.aspx
    v:130929\s.zaglio: sys.fn_varbintohexstr is too slow with big data
    v:130707\s.zaglio: added alias tag
    v:100805\s.zaglio: managed null @value
    v:091018\s.zaglio: convert a binary value into a hex string
    t:
        declare @s sysname set @s=''hello world!!''+char(13)+char(10)
        print dbo.fn__hex(convert(varbinary(128),@s))
        0x680065006c006c006f00200077006f0072006c006400210021000d000a00
*/
CREATE function [dbo].[fn__hex] (
    @value varbinary(max)
    )
returns varchar(max)
as
begin
return ''0x'' +
       cast('''' as xml).value(''xs:hexBinary(sql:variable("@value") )'',
                             ''varchar(max)'');
end -- fn__hex'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__hex:

-- ================================================================= fn__hex2bin
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__hex2bin',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130228
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__hex2bin') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__hex2bin') with nowait
        goto skip_fn__hex2bin
        end
    if @ver>130228
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__hex2bin') with nowait
        goto skip_fn__hex2bin
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__hex2bin') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__hex2bin'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__hex2bin]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130228\s.zaglio: convert a hex string to binary
    t:select dbo.fn__hex2bin(''01234ab'') -- return null
    t:select dbo.fn__hex2bin(''0x1234ab'') -- return 0x1234AB
    t:select dbo.fn__hex2bin(''1234ab'') -- return 0x1234AB
*/
CREATE function fn__hex2bin(
    @hex varchar(max)
)
returns varbinary(max)
as
begin
   return(select cast('''' as xml).value(''xs:hexBinary( substring(sql:variable("@hex"), sql:column("t.pos")) )'', ''varbinary(max)'')
   from (select case substring(@hex, 1, 2) when ''0x'' then 3 else 0 end) as t(pos))
end -- fn__hex2bin'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__hex2bin:

-- ================================================================= fn__hex2int
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__hex2int',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110627
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__hex2int') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__hex2int') with nowait
        goto skip_fn__hex2int
        end
    if @ver>110627
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__hex2int') with nowait
        goto skip_fn__hex2int
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__hex2int') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__hex2int'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__hex2int]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110627\s.zaglio:convert hex to int
    t:print dbo.fn__hex2int(''0x80000005'') -- -2147483643
    t:print dbo.fn__hex2int(''0x8000005'')  -- 134217733
*/
CREATE function fn__hex2int(@v varchar(16))
returns int
as
begin
if left(@v,2)!=''0x'' return null
else select @v=upper(substring(@v,3,8))
-- declare @v varchar(10) select @v=''80000005''
declare @vc as varchar(4),@i int,@cl char,@ch char
select @vc='''',@i=len(@v)
if @i%2=1 select @v=''0''+@v,@i=@i+1
-- exec sp__printf ''@i=%d @v=%s'',@i,@v
while (@i>0)
    begin
    select
        @ch=substring(@v,@i-1,1),@cl=substring(@v,@i,1),
        @vc=char(
                case
                when @cl between ''0'' and ''9'' then ascii(@cl)-48
                when @cl between ''A'' and ''F'' then ascii(@cl)-55
                end
                +
                case
                when @ch between ''0'' and ''9'' then (ascii(@ch)-48)*16
                when @ch between ''A'' and ''F'' then (ascii(@ch)-55)*16
                end
            )+@vc,
        @i=@i-2
    -- exec sp__printf ''@cl=%s @ch=%s @i=%d @vc=%s'',@cl,@ch,@i,@vc
    end
return cast(cast(@vc as binary(4)) as int)
end -- fn__hex2int'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__hex2int:

-- ================================================================== fn__inject
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__inject',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080414
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__inject') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__inject') with nowait
        goto skip_fn__inject
        end
    if @ver>080414
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__inject') with nowait
        goto skip_fn__inject
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__inject') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__inject'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__inject]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080414\S.Zaglio:inculate a string or sql
*/
CREATE   function [dbo].[fn__inject](
    @sql nvarchar(4000)
)
returns nvarchar(4000)
AS
begin
set @sql=replace(@sql,'''''''','''''''''''')
return @sql
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__inject:

-- ================================================================== fn__inlike
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__inlike',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100228
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__inlike') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__inlike') with nowait
        goto skip_fn__inlike
        end
    if @ver>100228
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__inlike') with nowait
        goto skip_fn__inlike
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__inlike') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__inlike'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__inlike]

begin try
exec dbo.sp_executesql @statement = N'/*    leave this
    l:see LICENSE file
    g:utility
    v:100228\s.zaglio: test @s with parts of @likes separated by |
    t:print dbo.fn__inlike(''me'',''you|me|us'')    --> 1
    t:print dbo.fn__inlike(''it'',''you|me|us'')    --> 0
    t:print dbo.fn__inlike(''you'',''your|%me%|us'')    --> 0
*/
create function fn__inlike(@s nvarchar(4000),@likes nvarchar(4000))
returns bit
as
begin
/*
declare @s nvarchar(4000),@likes nvarchar(4000)
select @s=''b'',@likes=''a|b|c''
--*/
declare @like nvarchar(4000),@i int,@n int,@noperc bit
if charindex(''%'',@s)=0 select @noperc=1
select @i=1,@n=dbo.fn__str_count(@likes,''|'')
while (@i<=@n)
    begin
    select @like=dbo.fn__str_at(@likes,''|'',@i)
    if @noperc=1 select @like=''%''+@like+''%''
    if @s like @like return 1
    select @i=@i+1
    end
return 0
end -- fn__inlike'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__inlike:

-- ================================================================== fn__int2ip
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__int2ip',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120904
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__int2ip') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__int2ip') with nowait
        goto skip_fn__int2ip
        end
    if @ver>120904
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__int2ip') with nowait
        goto skip_fn__int2ip
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__int2ip') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__int2ip'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__int2ip]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:ip,int,convert
    v:120904\s.zaglio:from http://www.mssqltips.com/sqlservertip/2535/
    t:print dbo.fn__int2ip(167772161) -- 10.0.0.1
*/
CREATE function fn__int2ip(@ip bigint)
returns varchar(15)
as
begin
declare @octet1 tinyint
declare @octet2 tinyint
declare @octet3 tinyint
declare @octet4 tinyint
declare @restofip bigint
set @octet1 = @ip / 16777216
set @restofip = @ip - (@octet1 * 16777216)
set @octet2 = @restofip / 65536
set @restofip = @restofip - (@octet2 * 65536)
set @octet3 = @restofip / 256
set @octet4 = @restofip - (@octet3 * 256)
return(
    convert(varchar, @octet1) + ''.'' +
    convert(varchar, @octet2) + ''.'' +
    convert(varchar, @octet3) + ''.'' +
    convert(varchar, @octet4)
    )
end -- fn__int2ip'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__int2ip:

-- ================================================================== fn__ip2int
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__ip2int',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120904
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__ip2int') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__ip2int') with nowait
        goto skip_fn__ip2int
        end
    if @ver>120904
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__ip2int') with nowait
        goto skip_fn__ip2int
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__ip2int') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__ip2int'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__ip2int]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:ip,int,convert
    v:120904\s.zaglio:from http://www.mssqltips.com/sqlservertip/2535/
    t:print dbo.fn__ip2int(''10.0.0.1'')
*/
create function fn__ip2int(@ip varchar(15))
returns bigint  -- for future ipv6
as
begin
return(
convert(bigint, parsename(@ip,1)) +
convert(bigint, parsename(@ip,2)) * 256 +
convert(bigint, parsename(@ip,3)) * 65536 +
convert(bigint, parsename(@ip,4)) * 16777216
)
end -- fn__int2ip'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__ip2int:

-- =============================================================== fn__isConsole
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__isConsole',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131208
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__isConsole') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__isConsole') with nowait
        goto skip_fn__isConsole
        end
    if @ver>131208
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__isConsole') with nowait
        goto skip_fn__isConsole
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__isConsole') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__isConsole'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__isConsole]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:is,console,ssms,app_name,test
    v:131208\s.zaglio:added check of SSDT
    v:130106.1000\s.zaglio:return 1 if app_name is a console like SSMS
    t:select dbo.fn__isConsole()
*/
CREATE function fn__isConsole()
returns bit
as
begin
-- works only locally because app_name() of a remote call is
-- mssql or ms.client or ...
if left(app_name(),39)=''Microsoft SQL Server Management Studio''
or left(app_name(),31)=''Microsoft SQL Server Data Tools''
    return 1
return 0
end -- fn__isConsole'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__isConsole:

-- ================================================================== fn__isGuid
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__isGuid',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090807
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__isGuid') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__isGuid') with nowait
        goto skip_fn__isGuid
        end
    if @ver>090807
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__isGuid') with nowait
        goto skip_fn__isGuid
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__isGuid') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__isGuid'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__isGuid]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090807\S.Zaglio: added remove of '',",{,}
    v:090604\S.Zaglio: test a guid
    c:originally from: http://jesschadwick.blogspot.com/2007/11/safe-handling-of-uniqueidentifier-in.html
    t: print dbo.fn__isguid(convert(sysname,newid()))
    t: print dbo.fn__isguid(replace(convert(sysname,newid()),''-'',''_''))
    t: print convert(uniqueidentifier,convert(sysname,newid()))
*/
CREATE function [dbo].[fn__isGuid](@input nvarchar(48))
returns bit
as
begin
declare @isvalidguid bit; set @isvalidguid = 0;
select @input=replace(@input, ''-'', '''')
select @input=replace(@input, '''''''', '''')
select @input=replace(@input, ''"'', '''')
select @input=replace(@input, ''{'', '''')
select @input=replace(@input, ''}'', '''')
set @input = upper(ltrim(rtrim(@input)));
if(@input is not null and len(@input) = 32)
    begin
    declare @indexchar nchar(1)
    declare @index int;  set @index = 1;
    while (@index <= 32)
        begin
        set @indexchar = substring(@input, @index, 1);
        if (isnumeric(@indexchar) = 1 or @indexchar in (''a'', ''b'', ''c'', ''d'', ''e'', ''f''))
            set @index = @index + 1;
        else
            break;
        end
    if(@index = 33)   set @isvalidguid = 1;
    end
return @isvalidguid;
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__isGuid:

-- =================================================================== fn__isJob
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__isJob',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110321
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__isJob') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__isJob') with nowait
        goto skip_fn__isJob
        end
    if @ver>110321
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__isJob') with nowait
        goto skip_fn__isJob
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__isJob') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__isJob'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__isJob]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110321\s.zaglio:added italian "SERVIZIO DI RETE"
    v:110213\s.zaglio:added SQLServerAgentSVC and exclusion of instance
    v:101211\s.zaglio:return 1 if the @spid is executed from a job
*/
CREATE function fn__isJob(@spid int)
returns bit
as
begin
if exists(
    select spid,program_name,hostname,nt_username
    from master..sysprocesses
    where spid=@spid
    and left([program_name],8)=''SQLAgent''
    and hostname=left(@@servername,len(hostname)) -- istance is not passed
    and ltrim(rtrim(nt_username)) in (''SYSTEM'',''SQLServerAgentSVC'',''SERVIZIO DI RETE'')
    )
    return 1
return 0
end -- fn__isJob'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__isJob:

-- ================================================================ fn__islocked
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__islocked',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__islocked') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__islocked') with nowait
        goto skip_fn__islocked
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__islocked') with nowait
        goto skip_fn__islocked
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__islocked') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__islocked'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__islocked]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091018\s.zaglio: added @db_id  (sp__find ''fn__islocked'')
    v:090910\s.zaglio: check if an object is locked
    t:print dbo.fn__islocked(db_id(''msdb''),object_id(''sysobjects''))
*/
CREATE function [dbo].[fn__islocked](@id int,@db_id int)
returns bit
as
begin
declare @locked bit
select @locked=0
if exists(
    -- sp_lock
    select     null
        /*convert (smallint, req_spid) As spid,
        rsc_dbid As dbid,
        rsc_objid As ObjId,
        rsc_indid As IndId,
        substring (v.name, 1, 4) As Type,
        substring (rsc_text, 1, 16) as Resource,
        substring (u.name, 1, 8) As Mode,
        substring (x.name, 1, 5) As Status
        */
    from
        master.dbo.syslockinfo,
        master.dbo.spt_values v,
        master.dbo.spt_values x,
        master.dbo.spt_values u
    where
        rsc_dbid=@db_id and
        rsc_objid=@id and
        master.dbo.syslockinfo.rsc_type = v.number
        and v.type = ''LR''
        and master.dbo.syslockinfo.req_status = x.number
        and x.type = ''LS''
        and master.dbo.syslockinfo.req_mode + 1 = u.number
        and u.type = ''L''
    /*order by spid*/     )     select @locked=1 return @locked end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__islocked:

-- =============================================================== fn__isMSSQL2K
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__isMSSQL2K',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110629
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__isMSSQL2K') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__isMSSQL2K') with nowait
        goto skip_fn__isMSSQL2K
        end
    if @ver>110629
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__isMSSQL2K') with nowait
        goto skip_fn__isMSSQL2K
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__isMSSQL2K') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__isMSSQL2K'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__isMSSQL2K]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110629\s.zaglio: optimized
    v:100919\s.zaglio: more compatible mssql2k
    v:100204\s.zaglio: return 1 if this is mssql 2000
    t:select @@version,dbo.fn__isMSSQL2K(),substring(@@version,23,4)
    t:
        declare @d datetime,@i int,@r nvarchar(4)
        select @i=10000
        exec sp__elapsed @d out,''init''
        while @i>0 select @r=substring(@@version,8,4),@i=@i-1  -- faster
        exec sp__elapsed @d out,''after @@ver''
        select @i=10000
        while @i>0 select @r=substring(cast(serverproperty(''productversion'') as sysname),1,1),@i=@i-1
        exec sp__elapsed @d out,''after prop''
*/
CREATE function fn__isMSSQL2K()
returns bit
as
begin
if substring(@@version,23,4) in (''2000'',''000 '') return 1
-- Microsoft SQL Server  2000 - 8.00.2039 ...
/*  ''Microsoft SQL Server 2000'',
    ''Microsoft SQL Server  200''
    )
    return 1
*/
return 0
end -- fn__isMSSQL2K()'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__isMSSQL2K:

-- =============================================================== fn__isnumeric
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__isnumeric',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130729
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__isnumeric') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__isnumeric') with nowait
        goto skip_fn__isnumeric
        end
    if @ver>130729
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__isnumeric') with nowait
        goto skip_fn__isnumeric
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__isnumeric') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__isnumeric'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__isnumeric]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    c:http://classicasp.aspfaq.com/general/what-is-wrong-with-isnumeric.html
    c:http://codecorner.galanter.net/2009/04/03/tsql-isnumeric-function-returns-false-positives/
    v:130729\s.zaglio:new concept
    v:120305\s.zaglio:bug near hexadecimal
    v:120213\s.zaglio:added hexadecimal
    v:110627\s.zaglio:extend mssql isnumeric
    t:print dbo.fn__isnumeric(''test test'') -- 0
    t:print dbo.fn__isnumeric(0x80035C56) -- 0
    t:print dbo.fn__isnumeric(''0xfffe'') -- 1
    t:print dbo.fn__isnumeric(''0xfffx'') -- 0
    t:print dbo.fn__isnumeric(''0xfx00'') -- 0
    t:print dbo.fn__isnumeric(''0.123'')  -- 1
    t:select isnumeric(''0.123.23''),dbo.fn__isnumeric(''0.123.23'') print convert(float,''0.123.23'')
    t:print isnumeric(''0,123'')    print convert(float,''0,123'') -- error
    t:print dbo.fn__isnumeric(''0,123,23'') -- 0
    todo:manage numbers with ,,,...
*/
CREATE function fn__isnumeric(@vs nvarchar(4000))
returns bit
as
begin
declare @i int,@n int
if left(@vs,2)=''0x''
    if substring(@vs,3,4000) like ''%[^0-9abcdef]%'' return 0
    else return 1

if charindex(''e'', @vs)!=0 return isnumeric(@vs)
else return isnumeric(@vs+''e0'')

return null
end -- fn__isnumeric'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__isnumeric:

-- ================================================================ fn__isRemote
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__isRemote',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120103
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__isRemote') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__isRemote') with nowait
        goto skip_fn__isRemote
        end
    if @ver>120103
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__isRemote') with nowait
        goto skip_fn__isRemote
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__isRemote') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__isRemote'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__isRemote]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120103\s.zaglio:seems to works
    r:101211\s.zaglio:return 1 if the sp is called from a remote server
    c:replace @@remserver
*/
CREATE function fn__isRemote(@spid int)
returns bit
as
begin
if exists(
    select null
    from master..sysprocesses
    where spid=@spid
    and app_name()=''Microsoft SQL Server''
    -- and left([program_name],8)=''SQLAgent''
    -- and hostname=@@servername
    and nt_username=''''
    and nt_domain=''''
    )
    return 1
return 0
end -- fn__isRemote'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__isRemote:

-- ==================================================================== fn__left
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__left',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=081010
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__left') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__left') with nowait
        goto skip_fn__left
        end
    if @ver>081010
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__left') with nowait
        goto skip_fn__left
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__left') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__left'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__left]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:081010\S.Zaglio: extend left with negative len
    t:print dbo.fn__left(''without last char*'',-1) -->without last char
*/
create function fn__left(@s nvarchar(4000),@l smallint)
returns nvarchar(4000)
as
begin
declare @s1 nvarchar(4000)
if @s is null return null
if @l=0 return ''''
if @l>0 set @s1=left(@s,@l)
else set @s1=left(@s,len(@s)+@l)
return @s1
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__left:

-- ================================================================ fn__max_date
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__max_date',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121023
        begin
        if @aut!='M.Guion'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__max_date') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__max_date') with nowait
        goto skip_fn__max_date
        end
    if @ver>121023
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__max_date') with nowait
        goto skip_fn__max_date
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__max_date') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__max_date'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__max_date]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    g:utility
    v:121023\M.Guion: restituisce la maggiore tra due date
*/
CREATE FUNCTION [dbo].[fn__max_date] (
    @date1 datetime,
    @date2 datetime
) RETURNS datetime
AS
BEGIN
    IF (ISNULL(@date1, ''19000101'') >= isnull(@date2, ''19000101''))
        RETURN @date1

    RETURN @date2
END'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__max_date:

-- =================================================================== fn__md5_r
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__md5_r',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110422
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__md5_r') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__md5_r') with nowait
        goto skip_fn__md5_r
        end
    if @ver>110422
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__md5_r') with nowait
        goto skip_fn__md5_r
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__md5_r') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__md5_r'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__md5_r]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110422\s.zaglio: used by sp__md5 or fn__md5
*/
create function [dbo].[fn__md5_r](@r SMALLINT, @a bigint, @b bigint, @c bigint, @d bigint, @x bigint, @s bigint, @t bigint )
returns bigint
as
begin
declare @r0 bigint, @r1 bigint

if @r=0 begin
    select @r0 = ( ( ( @c ^ @d ) & @b ) ^ @d )
    select @r1 = ( @a + @r0 + @x + @t ) & 0x000000000ffffffff
    select @r0 = ( ( @r1 * @s ) | cast( @r1 * @s / 4294967296 as bigint ) + @b ) & 0x000000000ffffffff
end
if @r=1 begin
    select @r0 = ( ( @b ^ @c ) & @d ) ^ @c
    select @r1 = ( @a + @r0 + @x + @t ) & 0x000000000ffffffff
    select @r0 = ( ( @r1 * @s ) | cast( @r1 * @s / 4294967296 as bigint ) + @b ) & 0x000000000ffffffff
end
if @r=2 begin
    select @r0 = @b ^ @c ^ @d
    select @r1 = ( @a + @r0 + @x + @t ) & 0x00000000ffffffff
    select @r0 = ( ( @r1 * @s ) | cast( @r1 * @s / 4294967296 as bigint ) + @b ) & 0x00000000ffffffff
end
if @r=3 begin
    select @r0 = ( ( ~@d ) | @b ) ^@c
    select @r1 = ( @a + @r0 + @x + @t ) & 0x00000000ffffffff
    select @r0 = ( ( @r1 * @s ) | cast( @r1 * @s / 4294967296 as bigint ) + @b ) & 0x00000000ffffffff
end
return  @r0
end -- fn__md5_r'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__md5_r:

-- ============================================================= fn__object_type
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__object_type',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090606
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__object_type') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__object_type') with nowait
        goto skip_fn__object_type
        end
    if @ver>090606
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__object_type') with nowait
        goto skip_fn__object_type
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__object_type') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__object_type'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__object_type]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    v:090606\S.Zaglio: return object definition (int,varchar,etc. if column; V,C,U,etc. if not)
    g:utility
    t:print dbo.fn__object_type(''sysobjects'')
    t:print dbo.fn__object_type(''sysobjects.name'')
    t:print dbo.fn__object_type(''test.txt'')
*/
CREATE function [dbo].[fn__object_type](@name sysname)
returns sysname
as
begin
declare @obj  sysname
declare @col  sysname
declare @type sysname

select @obj=dbo.fn__str_at(@name,''.'',1),
       @col=dbo.fn__str_at(@name,''.'',2)

if @col is null select top 1 @type=xtype from sysobjects where id=object_id(@name)
else select top 1 @type=t.name from syscolumns c inner join systypes t on c.xusertype=t.xusertype where c.id=object_id(@obj) and c.name=@col

return @type
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__object_type:

-- ============================================================== fn__occurrence
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__occurrence',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100228
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__occurrence') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__occurrence') with nowait
        goto skip_fn__occurrence
        end
    if @ver>100228
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__occurrence') with nowait
        goto skip_fn__occurrence
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__occurrence') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__occurrence'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__occurrence]

begin try
exec dbo.sp_executesql @statement = N'/*
    leave this
    l:see LICENSE file
    g:utility
    v:100228\s.zaglio: count occurrence of @seq in @s
    todo:deprecate to fn__occurrences
    t:print dbo.fn__occurrence('''','','')          --> 0
    t:print dbo.fn__occurrence(''a,b,c'','','')     --> 2
    t:print dbo.fn__occurrence(''a'','','')         --> 0
    t:print dbo.fn__occurrence(''a,b'','','')       --> 1
*/
CREATE function [dbo].[fn__occurrence](@s nvarchar(4000),@seq nvarchar(32))
returns int
as
begin
    declare @i int set @i=0
    declare @n int set @n=0
    if @s is null return null
    if @s='''' return 0
    declare @k int set @k=len(@seq)
    select @i=charindex(@seq,@s)
    while @i>0
        begin
        select @n=@n+1
        select @i=charindex(@seq,@s,@i+@k)
        end -- while
    return @n
end -- fn__occurrence'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__occurrence:

-- =========================================================== fn__plan_calendar
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__plan_calendar',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121118
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__plan_calendar') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__plan_calendar') with nowait
        goto skip_fn__plan_calendar
        end
    if @ver>121118
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__plan_calendar') with nowait
        goto skip_fn__plan_calendar
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__plan_calendar') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__plan_calendar'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__plan_calendar]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,plan
    k:plan,calendar,planning
    r:121118\s.zaglio: store a date into the plan calendar strtucture
    t:sp__plan_calendar_test
*/
CREATE function fn__plan_calendar(
    @dt datetime,
    @days nvarchar(24)    -- 12 int:bit0=01/01
    )
returns nvarchar(24)
as
begin
/*
declare
    @dt datetime,
    @days varchar(48)    -- 12 int:bit0=01/01
select
    @dt=''2012-01-02'',       -- ydm
    @days=''''
*/
declare
    @l int,
    @pmm int,               -- byte position of month
    @dd bigint,
    @chunk binary(4)     -- days buffer

select
    @l=datalength(@days),
    @pmm=(month(@dt)-1)*2+1,
    @dd=power(2,day(@dt)-1)

-- select @dt dt,@days days,@l l,@pmm pmm,@dd dd

if @l<@pmm+2
    select @days=@days+replicate(char(0),@pmm+2-1-@l)

select
    @chunk=cast(substring(@days,@pmm,2) as binary(4))|@dd

-- select datalength(@days) len_days,@chunk chunk

/*
select @days=
    convert(varbinary(48),
        stuff(
            convert(varchar(48),@days),
            @pmm,
            4,
            convert(varchar(4),@chunk)
            )
        )
*/
select @days=substring(@days,1,@pmm-1)+cast(@chunk as nchar(2))
            +substring(@days,@pmm+2,48)

-- select cast(@days as varbinary(48)) result

return @days
end -- fn__plan_calendar'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__plan_calendar:

-- ================================================================== fn__printf
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__printf',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130725
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__printf') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__printf') with nowait
        goto skip_fn__printf
        end
    if @ver>130725
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__printf') with nowait
        goto skip_fn__printf
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__printf') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__printf'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__printf]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130725,130724\s.zaglio: a bug in particolar condition from sp__elapsed
    v:130614\s.zaglio: added %x and varchar max
    v:110429\s.zaglio: added special case for *binary
    v:100918\s.zaglio: again better print on small float and real
    v:100914\s.zaglio: better precision on datetime,real, etc.
    v:081021\S.Zaglio: corrected bad replace in case @p? contains %s
    v:080925\S.Zaglio: replace xp_printf because truncate strings to +/-1000 chars
    v:080515\S.Zaglio: as sp__printf but only for functions debug, don''t print
    t:sp__printf_test @opt=''run'',@dbg=1
    t:print dbo.fn__printf(''test (%s)'',''%drop%'',null,null,null,null,null,null,null,null,null)
*/
CREATE function fn__printf(
    @format nvarchar(max),
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null,
    @p5 sql_variant=null,
    @p6 sql_variant=null,
    @p7 sql_variant=null,
    @p8 sql_variant=null,
    @p9 sql_variant=null,
    @p0 sql_variant=null)
returns nvarchar(max)
as
begin
declare
    @p sql_variant,@s nvarchar(4000),
    @crlf nvarchar(2),@tab nvarchar(2),
    @i int,@n int,@type char,@ln int

select @crlf=crlf,@tab=tab,@n=1 from fn__sym()
select
    @p1=isnull(@p1,''(null)''),   @p2=isnull(@p2,''(null)''),
    @p3=isnull(@p3,''(null)''),   @p4=isnull(@p4,''(null)''),
    @p5=isnull(@p5,''(null)''),   @p6=isnull(@p6,''(null)''),
    @p7=isnull(@p7,''(null)''),   @p8=isnull(@p8,''(null)''),
    @p9=isnull(@p9,''(null)''),   @p0=isnull(@p0,''(null)'')

select @format=replace(@format,''%d'',''%s'')
select @format=replace(@format,''\n'',@crlf)
select @format=replace(@format,''\t'',@tab)

select @i=charindex(''%'',@format),@ln=len(@format)
while (@i>0)
    begin
    select @type=substring(@format,@i+1,1)
    if @type in (''s'',''x'',''d'')
        begin
        select @p=(case @n
            when 1 then @p1 when 2 then @p2 when 3 then @p3 when 4 then @p4 when 5 then @p5
            when 6 then @p6 when 7 then @p7 when 8 then @p8 when 9 then @p9 when 10 then @p0 end)
        if @type!=''x'' and sql_variant_property(@p,''BaseType'') in (''datetime'',''smalldatetime'')
            select @s=convert(nvarchar(4000),@p,126)
        else
            if @type!=''x'' and sql_variant_property(@p,''BaseType'') in (''real'',''float'')
                begin
                select @s=convert(nvarchar(4000),@p,0)
                if charindex(''e-'',@s)>0 select @s=convert(nvarchar(4000),@p,1)
                if charindex(''e-'',@s)>0 select @s=convert(nvarchar(4000),@p,2)
                end
            else
            if sql_variant_property(@p,''BaseType'') in (''binary'',''varbinary'')
            or @type=''x''
                select @s=sys.fn_varbintohexstr(convert(varbinary(8000),@p))
        else
            select @s=convert(nvarchar(4000),@p)

        select @format=left(@format,@i-1)+@s+substring(@format,@i+2,@ln)
        select @i=@i+len(@s)
        select @n=@n+1
        end
    else
        if @type=''%''
            select @format=left(@format,@i)+substring(@format,@i+2,@ln),
                   @i=@i+1
        else
            select @i=@i+1

        select @i=charindex(''%'',@format,@i)
    end -- while
-- the xp_sprintf truncate to less than 4000 chars
-- exec master..xp_sprintf @format out,@format,@p1,@p2,@p3,@p4,@p5,@p6,@p7,@p8,@p9,@p0
return @format
end -- fn__printf'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__printf:

-- ================================================================== fn__prints
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__prints',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130612
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__prints') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__prints') with nowait
        goto skip_fn__prints
        end
    if @ver>130612
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__prints') with nowait
        goto skip_fn__prints
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__prints') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__prints'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__prints]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:prints,sp__prints,separator,better,code,read
    v:130612\s.zaglio: used by sp__prints
    t:select dbo.fn__prints(''8<test'',default,default,default,default)
    t:select dbo.fn__prints(''test'',default,default,default,default)
*/
CREATE function fn__prints(
    @comment nvarchar(4000)=null,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null
    )
returns nvarchar(4000)
as
begin
if @comment is null return null

declare @line nvarchar(80)

if left(@comment,2)=''8<''
    begin
    select @line=''-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --''
    select @comment=substring(@comment,3,128)
    if @comment!='''' select @line=left(@line,80-len(@comment)-4)+'' ''+@comment+'' --''
    return @line
    end

select @comment=dbo.fn__printf(@comment,@p1,@p2,@p3,@p4,null,null,null,null,null,null)
select @line=replicate(''='',76)
select @line=''-- =''+replicate(''='',72-len(@comment))+'' ''+@comment+'' ==''
return @line

end -- fn__prints'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__prints:

-- ============================================================== fn__RegexMatch
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__RegexMatch',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131009
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__RegexMatch') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__RegexMatch') with nowait
        goto skip_fn__RegexMatch
        end
    if @ver>131009
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__RegexMatch') with nowait
        goto skip_fn__RegexMatch
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__RegexMatch') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__RegexMatch'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__RegexMatch]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131009\s.zaglio:restyle
    v:080101\www.simple-talk.com
    c:probably the best tutorial on the web for regular expressions is on www.regular-expressions.info
    c:but it is also worth reading implementing real-world data input validation using regular expressions by francis norton
    c:for an introduction to regular expressions:
    c:http://www.simple-talk.com/dotnet/.net-framework/implementing-real-world-data-input-validation-using-regular-expressions/
    t:
        -- now, with this routine, we can do some complex input validation
        --is there a repeating word
        select dbo.fn__regexmatch(''\b(\w+)\s+\1\b'',''this has has been repeated'')--1
        select dbo.fn__regexmatch(''\b(\w+)\s+\1\b'',''this has not been repeated'')--0

        --find a word near another word (in this case ''for'' and ''last'' 1 or 2 words apart)
        select dbo.fn_regexmatch(''\bfor(?:\w+\w+){1,2}?\w+last\b'',
                   ''you have failed me for the last time, admiral'')--1
        select dbo.fn__regexmatch(''\bfor(?:\w+\w+){1,2}?\w+last\b'',
                   ''you have failed me for what could be the last time, admiral'')--0

        --is this likely to be a valid credit card
        select dbo.fn__regexmatch(''^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6011[0-9]{12}|3(?:0
        [0-5]|[68][0-9])[0-9]{11}|3[47][0-9]{13}|(?:2131|1800)\d{11})$'',''4953129482924435'')

        --is this a valid zip code
        select dbo.fn__regexmatch(''^[0-9]{5,5}([- ]?[0-9]{4,4})?$'',''02115-4653'')

        --is this a valid postcode
        select dbo.fn__regexmatch(''^([gg][ii][rr] 0[aa]{2})|((([a-za-z][0-9]{1,2})|(([a-za-z][a-ha
        -hj-yj-y][0-9]{1,2})|(([a-za-z][0-9][a-za-z])|([a-za-z][a-ha-hj-yj-y][0-9]?[a-za-z])))
        ) {0,1}[0-9][a-za-z]{2})$'',''rg35 2aq'')

        --is this a valid european date
        select dbo.fn__regexmatch(''^((((31\/(0?[13578]|1[02]))|((29|30)\/(0?[1,3-9]|1[0-2])))\/(1[
        6-9]|[2-9]\d)?\d{2})|(29\/0?2\/(((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16
        |[2468][048]|[3579][26])00))))|(0?[1-9]|1\d|2[0-8])\/((0?[1-9])|(1[0-2]))\/((1[6-9]|[2
        -9]\d)?\d{2})) (20|21|22|23|[0-1]?\d):[0-5]?\d:[0-5]?\d$'',''12/12/2007 20:15:27'')

        --is this a valid currency value (dollar)
        select dbo.fn__regexmatch(''^\$(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$'',''$34,000.00'')

        --is this a valid currency value (sterling)
        select dbo.fn__regexmatch(''^\&pound;(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$'',
        ''&pound;34,000.00'')

        --a valid email address?
        select dbo.fn__regexmatch(''^(([a-za-z0-9!#\$%\^&\*\{\}''''`\+=-_\|/\?]+(\.[a-za-z0-9!#\$%\^&
        \*\{\}''''`\+=-_\|/\?]+)*){1,64}@(([a-za-z0-9]+[a-za-z0-9-_]*){1,63}\.)*(([a-za-z0-9]+[a
        -za-z0-9-_]*){3,63}\.)+([a-za-z0-9]{2,4}\.?)+){1,255}$'',''phil.factor@simple-talk.com'')
*/
CREATE function dbo.fn__regexmatch
    (
      @pattern nvarchar(2000),
      @matchstring nvarchar(max)--varchar(8000) got sql server 2000
    )
returns int
/* the regexmatch returns true or false, indicating if the regular expression
matches (part of) the string. (it returns null if there is an error).
when using this for validating user input, you''ll normally want to check
if the entire string matches the regular expression. to do so, put a caret
at the start of the regex, and a dollar at the end, to anchor the regex at
the start and end of the subject string.
*/
-- with this function, the passing back of errors is rudimentary.
-- if an ole error occurs, then a null is passed back.
as
begin
declare @objregexexp int,
    @objerrorobject int,
    @strerrormessage nvarchar(255),
    @hr int,
    @match bit

select  @strerrormessage = ''creating a regex object''
exec @hr= sp_oacreate ''vbscript.regexp'', @objregexexp out
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''pattern'', @pattern
    --specifying a case-insensitive match
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''ignorecase'', 1
    --doing a test''
if @hr = 0
    exec @hr= sp_oamethod @objregexexp, ''test'', @match out, @matchstring
if @hr <> 0
    begin
        return null
    end
exec sp_oadestroy @objregexexp
return @match
end -- fn__regexmatch'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__RegexMatch:

-- ============================================================ fn__RegexReplace
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__RegexReplace',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131009
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__RegexReplace') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__RegexReplace') with nowait
        goto skip_fn__RegexReplace
        end
    if @ver>131009
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__RegexReplace') with nowait
        goto skip_fn__RegexReplace
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__RegexReplace') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__RegexReplace'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__RegexReplace]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131009\s.zaglio:restyle
    v:080101\www.simple-talk.com
    t:
        --remove repeated words in text
        SELECT  dbo.fn__RegexReplace(''\b(\w+)(?:\s+\1\b)+'', ''$1'',
                                 ''Sometimes I cant help help help stuttering'',1, 1)

        --find a #comment and add a TSQL --
        SELECT  dbo.fn__RegexReplace(''#.*'',''--$&'',''
        # this is a comment
        first,second,third,fourth'',1,1)

        --replace a url with an HTML anchor
        SELECT  dbo.fn__RegexReplace(
                ''\b(https?|ftp|file)://([-A-Z0-9+&@#/%?=~_|!:,.;]*[-A-Z0-9+&@#/%=~_|])'',
                ''<a href="$2">$2</a>'',
                 ''There is  this amazing site at http://www.simple-talk.com'',1,1)

        --strip all HTML elements out of a string
        SELECT  dbo.fn__RegexReplace(''<(?:[^>''''"]*|([''''"]).*?\1)*>'',
           '''',''<a href="http://www.simple-talk.com">Simle Talk is wonderful</a><!--This is a comment --> we all love it'',1,1)

        --import delimited ntext into a database, converting it into insert statements
        SELECT  dbo.fn__RegexReplace(
         ''([^\|\r\n]+)[|\r\n]+([^\|\r\n]+)[|\r\n]+([^\|\r\n]+)[|\r\n]+([^\|\r\n]+)[|\r\n]+'',
         ''Insert into MyTable (Firstcol,SecondCol, ThirdCol, Fourthcol)
        select $1,$2,$3,$4
        '',''1|white gloves|2435|24565
        2|Sports Shoes|285678|0987
        3|Stumps|2845|987
        4|bat|29862|4875'',1,1)
*/
CREATE FUNCTION dbo.fn__RegexReplace
    (
      @pattern nvarchar(255),
      @replacement nvarchar(255),
      @Subject nvarchar(4000),
      @global BIT = 1,
     @Multiline bit =1
    )
returns nvarchar(4000)

/*the regexreplace function takes three string parameters. the pattern
(the regular expression) the replacement expression, and the subject
string to do the manipulation to.

the replacement expression is one that can cause difficulties. you can
specify an empty string '''' as the @replacement text. this will cause the
replace method to return the subject string with all regex matches
deleted from it (see "strip all html elements out of a string" below).
to re-insert the regex match as part of the replacement, include $& in
the replacement text. (see "find a #comment and add a tsql --" below)
if the regexp contains capturing parentheses, you can use backreferences
in the replacement text. $1 in the replacement ntext inserts the ntext
matched by the first capturing group, $2 the second, etc. up to $9.
(e.g. see import delimited ntext into a database below) to include a
literal dollar sign in the replacements, put two consecutive dollar
signs in the string you pass to the replace method.*/
as
begin
declare @objregexexp int,
    @objerrorobject int,
    @strerrormessage nvarchar(255),
    @substituted nvarchar(4000),
    @hr int,
    @replace bit

select  @strerrormessage = ''creating a regex object''
exec @hr= sp_oacreate ''vbscript.regexp'', @objregexexp out
if @hr = 0
    select  @strerrormessage = ''setting the regex pattern'',
            @objerrorobject = @objregexexp
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''pattern'', @pattern
if @hr = 0 /*
              by default, the regular expression is case sensitive.
              set the ignorecase property to true to make it case insensitive.
           */
    select  @strerrormessage = ''specifying the type of match''
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''ignorecase'', 1
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''multiline'', @multiline
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''global'', @global
if @hr = 0
    select  @strerrormessage = ''doing a replacement''
if @hr = 0
    exec @hr= sp_oamethod @objregexexp, ''replace'', @substituted out,
        @subject, @replacement
/*if the regexp.global property is false (the default), replace will return
the @subject string with the first regex match (if any) substituted with
the replacement text. if regexp.global is true, the @subject string will
be returned with all matches replaced.*/
if @hr <> 0
    begin
        declare @source nvarchar(255),
            @description nvarchar(255),
            @helpfile nvarchar(255),
            @helpid int

        execute sp_oageterrorinfo @objerrorobject, @source output,
            @description output, @helpfile output, @helpid output
        select  @strerrormessage = ''error whilst ''
                + coalesce(@strerrormessage, ''doing something'') + '', ''
                + coalesce(@description, '''')
        return @strerrormessage
    end
exec sp_oadestroy @objregexexp
return @substituted
end -- fn__RegexReplace'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__RegexReplace:

-- ========================================================== fn__script_buildin
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__script_buildin',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110916
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__script_buildin') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__script_buildin') with nowait
        goto skip_fn__script_buildin
        end
    if @ver>110916
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__script_buildin') with nowait
        goto skip_fn__script_buildin
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_buildin') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_buildin'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_buildin]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110916\s.zaglio:added special case with @usr='''' and @comment=''''
    v:110824\s.zaglio:added @usr
    v:110325\s.zaglio:return buildin string for scripts
    t:select dbo.fn__script_buildin(getdate(),default,default,default)
    t:select dbo.fn__script_buildin(getdate(),0,''s.zaglio'',''test'')
    t:select dbo.fn__script_buildin(getdate(),1,'''','''')
    t:sp__find ''fn__script_buildin''
*/
CREATE function fn__script_buildin(
    @dt datetime,
    @time int,
    @usr sysname,
    @comment sysname
    )
returns sysname
as
begin
declare @s sysname
select @s=
    convert(sysname,@dt,12)+
    case
    when isnull(@time,1)=1
    then ''.''+left(replace(convert(sysname,@dt,8),'':'',''''),4)
    else ''''
    end+
    case
    when isnull(@usr,'''')='''' and isnull(@comment,'''')=''''
    then ''''
    else ''\''+isnull(@usr,system_user)+
         '':''+isnull(@comment,''%unspecified comment%'')
    end

return @s
end -- fn__script_buildin'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_buildin:

-- ============================================================== fn__servername
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__servername',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__servername') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__servername') with nowait
        goto skip_fn__servername
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__servername') with nowait
        goto skip_fn__servername
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__servername') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__servername'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__servername]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091018\s.zaglio: added ip recodnize
    v:090127\S.Zaglio: added domain and now this function check if @svr is local, ignoring the domain if necessary
    v:081121\S.Zaglio: replace @@servername that can fail with virtual svr name when there is a real svr with same name
    t: print dbo.fn__servername(null)
    t: print dbo.fn__servername(''server.domain\instance'') ->server.domain\instance
    t: print dbo.fn__servername(''server\instance'') ->server\instance
    t: print dbo.fn__servername(''ip\instance'') ->ip\instance
*/
CREATE function fn__servername(@svr sysname)
returns sysname
as
begin
declare @r sysname
-- set @r=coalesce(@@servername,convert(sysname,serverproperty(''servername'')))
-- I don''t remember why excluded @@servername
declare @key sysname,@domain sysname,@server sysname,@instance sysname,@ip sysname

select @ip=dbo.fn__global_get(''server_ip'')

SET @key = ''SYSTEM\ControlSet001\Services\Tcpip\Parameters\''
EXEC master..xp_regread @rootkey=''HKEY_LOCAL_MACHINE'', @key=@key,@value_name=''Domain'',@value=@Domain OUTPUT
-- SELECT ''Server Name: ''+@@servername + '' Domain Name:''+convert(varchar(100),@Domain)
set @server=convert(sysname,serverproperty(''servername''))
declare @i int set @i=charindex(''\'',@server)
if @i>0 begin
    set @instance=substring(@server,@i,len(@server))
    set @server=left(@server,@i-1)
    end
else set @instance=''''
set @r=@server+coalesce(''.''+@domain,'''')+@instance
if not @svr is null begin
    set @server=replace(@svr,''\'',''%\'')
    set @ip=@ip+@instance
    if @r like @server set @r=@svr -- return name witch to check
    if @svr like @ip set @r=@svr
end
return @r
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__servername:

-- ============================================================== fn__split_part
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__split_part',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120919
        begin
        if @aut!='A.Sirna'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__split_part') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__split_part') with nowait
        goto skip_fn__split_part
        end
    if @ver>120919
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__split_part') with nowait
        goto skip_fn__split_part
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__split_part') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__split_part'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__split_part]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    v:120919\A.Sirna: bugfix
    v:110801\A.Sirna: return specified part of splitted string
    t: select dbo.fn__split_part(''25:32'','':'',1)
*/
CREATE function dbo.fn__split_part(
    @s nvarchar(4000),
    @separator nvarchar(50),
    @part int
) returns nvarchar(4000)
as
begin

    if @part < 0 goto ret

    declare @index int
    declare @spart nvarchar(4000)
    set @index = 0

    while len(@s) > 0
    begin

        if charindex(@separator,@s) = 0
        begin
            if @index = @part
            begin
                set @spart = @s
                goto ret
            end

            set @spart = NULL
            goto ret
        end


        select
            @spart = substring(@s,0,charindex(@separator,@s)),
            @s = substring(@s,charindex(@separator,@s)+1,len(@s))

        if @index = @part
            goto ret
        else
            set @index = @index+1
    end

ret:
    return @spart
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__split_part:

-- ============================================================= fn__sql_def_col
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_def_col',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110324
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_def_col') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_def_col') with nowait
        goto skip_fn__sql_def_col
        end
    if @ver>110324
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_def_col') with nowait
        goto skip_fn__sql_def_col
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_def_col') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_def_col'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_def_col]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:110324\s.zaglio: adapted to mssql2k5
    v:100919\s.zaglio: a bug near binary size
    v:100404\s.zaglio: collate before null/not null
    v:100328\s.zaglio: bug near default
    v:100110\s.zaglio: return tsql column declaration
    t:see [fn__sql_def_tbl] test
*/
CREATE function [dbo].[fn__sql_def_col](
    @table      sysname,
    @new        sysname,
    @column     sysname,
    @utype      int,
    @type       sysname,
    @len        int,
    @prec       int,
    @scale      int,
    @nullable   bit,
    @default    sysname,
    @def_fx     sysname,
    @identity   bit,
    @computed   bit,
    @function   sysname,
    @chk        sysname,
    @chk_fx     sysname,
    @collation  sysname,
    @status     int,
    @filler     bit
)
returns nvarchar(4000)
as
begin
-- select * from systypes where xtype in (175,239,231,167)
-- select * from syscolumns c where c.type=108 id=object_id(''sp__printf'')
--
declare @def nvarchar(4000)
if @identity is null select @identity=case when @status & 0x80=0x80 then 1 else 0 end
if @new is null  select @new=@table
select @def=
    -- name
    @column+'' ''+
    -- type
    case when @computed=0
         then @type +
            case -- select * from systypes
            when @utype in (175,239,231,167) --char,nchar,nvc,vc
                 or @type in (''char'',''nchar'',''nvarchar'',''varchar'',''binary'',''varbinary'')
            then ''('' + case when @prec<0 then ''max'' else cast(@prec as nvarchar) end +'') ''
                                   -- (@len/case
                                   --when left(@column,1) =''n''
                                   --then 2 else 1 end) as nvarchar) + '') ''
            when @utype in (106,108) or @type in (''decimal'',''numeric'')
            then ''('' + cast(@prec as nvarchar)+'',''+
                       cast(@scale as nvarchar)+'') ''
         else '''' end +
    -- collate
    isnull('' collate ''+ @collation,'''')+
    -- nullable
    case when @nullable=1 and @identity!=1 then '' null '' else '' not null '' end +
    -- identity
    case when @identity = 1
         then '' identity('' +
                cast(ident_seed(@table) as nvarchar)+'',''+
                cast(ident_incr(@table) as nvarchar) + '') ''
         else '''' end +
    -- constrain
    isnull('' constraint ['' + @chk + ''_'' + @new+ ''] check '' + @chk_fx ,'''') +
    -- default
    isnull('' constraint '' + @default ,'''') +
    isnull('' default '' + @def_fx ,'''') +
    ''''
    -- computation
    else '' as '' + @function  end

return @def
end -- fn__sql_def_col'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_def_col:

-- ============================================================= fn__sql_def_typ
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_def_typ',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100321
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_def_typ') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_def_typ') with nowait
        goto skip_fn__sql_def_typ
        end
    if @ver>100321
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_def_typ') with nowait
        goto skip_fn__sql_def_typ
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_def_typ') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_def_typ'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_def_typ]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g: utility,script
    v: 100321\s.zaglio: return tsql type declaration
    t: see [fn__sql_def_col] test
*/
create function [dbo].[fn__sql_def_typ](
    @utype      int,
    @type       sysname,
    @len        int,
    @prec       int,
    @scale      int,
    @nullable   bit
)
returns nvarchar(4000)
as
begin
-- select * from systypes where xtype in (175,239,231,167)
-- select top 1 * from syscolumns
/*  select c.name,dbo.fn__sql_def_typ(c.xusertype,null,c.length,c.prec,c.scale,c.isnullable),c.*
    from syscolumns c
    where id=object_id(''sp__usage'')
*/
declare @def nvarchar(4000)
select @def=case when @type is null then type_name(@utype) else @type end+
            case -- select * from systypes
            when @utype in (175,239,231,167) --char,nchar,nvc,vc
            then ''('' + cast(@prec as nvarchar) +'') ''
                                   -- (@len/case
                                   --when left(@column,1) =''n''
                                   --then 2 else 1 end) as nvarchar) + '') ''
            when @utype in (106,108) then ''('' + cast(@prec as nvarchar)+'',''+
                                                cast(@scale as nvarchar)+'') ''
            else ''''
            end+
    -- nullable
    case when @nullable=1 then '' null '' else '' not null '' end
return @def
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_def_typ:

-- ============================================================== fn__sql_format
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_format',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091022
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_format') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_format') with nowait
        goto skip_fn__sql_format
        end
    if @ver>091022
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_format') with nowait
        goto skip_fn__sql_format
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_format') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_format'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_format]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091022\s.zaglio: better view
    v:080815\S.Zaglio: reformat a string that containt sql code
    t:
        begin
        declare @s nvarchar(500)
        set @s=''select     a,      b,       c,      from ''+char(13)+char(10)+''      table''
        set @s=dbo.fn__sql_format(@s,120)
        print @s
        end
*/
CREATE function [dbo].[fn__sql_format](@sql nvarchar(4000), @wrap smallint)
returns nvarchar(4000)
as
begin
declare @crlf nchar(2) select @crlf=char(13)+char(10)
set @sql=replace(lower(@sql),@crlf,'' '')
set @sql=replace(@sql,''update '',    @crlf+''UPDATE '')
set @sql=replace(@sql,''insert '',    @crlf+''INSERT '')
set @sql=replace(@sql,''select '',    @crlf+''SELECT '')
set @sql=replace(@sql,''set '',       ''SET '')
set @sql=replace(@sql,''right '',     ''RIGHT '')
set @sql=replace(@sql,''INNER '',     ''INNER '')
set @sql=replace(@sql,''outer '',     ''OUTER '')
set @sql=replace(@sql,''join '',      ''JOIN '')
set @sql=replace(@sql,''on '',        ''ON '')
set @sql=replace(@sql,''or '',        ''OR '')
set @sql=replace(@sql,''and '',       ''AND '')
set @sql=replace(@sql,''set '',       ''SET '')
set @sql=replace(@sql,''left '',      ''LEFT '')
set @sql=replace(@sql,''from '',      @crlf+''FROM '')
set @sql=replace(@sql,''into '',      ''INTO '')
set @sql=replace(@sql,''where '',     @crlf+''WHERE '')
set @sql=replace(@sql,''order by '',  @crlf+''ORDER BY '')
set @sql=replace(@sql,''group by '',  ''GROUP BY '')
return dbo.fn__str_split(dbo.fn__str_simplify(@sql,0),@wrap)
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_format:

-- =========================================================== fn__sql_normalize
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_normalize',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120913
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_normalize') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_normalize') with nowait
        goto skip_fn__sql_normalize
        end
    if @ver>120913
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_normalize') with nowait
        goto skip_fn__sql_normalize
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_normalize') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_normalize'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_normalize]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:normalize, simplify
    v:120913\s.zaglio: a bug in exp semplification
    v:120903\s.zaglio: added exp option
    v:120820\s.zaglio: used () around top %
    v:120809\s.zaglio: remove extra from code and put on single line
    d:120809\s.zaglio: fn__sql_simplify
    t:print dbo.fn__sql_normalize(''  test  ''+char(13)+''  '',default)
    t:print dbo.fn__sql_normalize(''  test  -- comment''+char(13)+''  '',default)
    t:print dbo.fn__sql_normalize(''  dt   desc'',''ord'')
    t:select dbo.fn__sql_normalize('' 20%'',''top'')
    t:select dbo.fn__sql_normalize('' 20'',''top'')
    t:
        select dbo.fn__sql_normalize('' t1.f1="a",
                                       t2.f2=t3.f3 '',
                                     ''exp'')
    t:
        select dbo.fn__sql_normalize('' t1.f1="a" and
                                       t2.f2=t3.f3 '',
                                     ''exp'')
*/
CREATE function fn__sql_normalize (
    @sql nvarchar(max),
    @opt sysname
)
returns nvarchar(max)
as
begin
declare
    @crlf   nvarchar(2),
    @cr     nvarchar(1),
    @lf     nvarchar(1),
    @tab    nvarchar(1),
    @sel    bit,                        -- sel option specified
    @ord    bit,                        -- ord option
    @top    bit,
    @exp    bit

if @sql is null return null
if charindex(''--'',@sql)>0 return null

select
    @cr=cr,@lf=lf,@tab=tab,@crlf=crlf,
    @opt=case
         when not @opt is null
         then dbo.fn__str_quote(isnull(@opt,''''),''|'')
         else null
         end
from dbo.fn__sym()

while charindex(@cr,@sql)>0 select @sql=replace(@sql,@cr,'' '')
while charindex(@lf,@sql)>0 select @sql=replace(@sql,@lf,'' '')
while charindex(@tab,@sql)>0 select @sql=replace(@sql,@tab,'''')
while charindex(''  '',@sql)>0 select @sql=replace(@sql,''  '','' '')
select @sql=ltrim(rtrim(@sql))

if @sql='''' return null

if not @opt is null
    begin
    select
        @sel=charindex(''|sel|'',@opt),
        @ord=charindex(''|ord|'',@opt),
        @top=charindex(''|top|'',@opt),
        @exp=charindex(''|exp|'',@opt)

    if @sel=1   -- expand table name to select
        begin
        if left(@sql,7)!=''select ''
            select @sql=''select top 100 percent * from ''+dbo.fn__str_quote(@sql,''[]'')
        end

    if @ord=1   -- expand to oby expression
        begin
        if left(@sql,9)!=''order by ''
            select @sql=''order by ''+@sql
        end

    if @top=1   -- convert n or n% to top ...
        begin
        if (right(@sql,1)=''%''  and isnumeric(left(@sql,len(@sql)-1))=0)
        or (right(@sql,1)!=''%'' and isnumeric(@sql)=0)
            select @sql=''#wrong number or percent''
        else
            begin
            if right(@sql,1)=''%''
                select @sql=''top (''+cast(left(@sql,len(@sql)-1) as sysname)
                           +'') percent''
            else
                select @sql=''top (''+cast(left(@sql,len(@sql)) as sysname)+'')''
            end
        end -- top

    if @exp=1   -- expression for join: replace " with '''' and strip spaces
        begin
        select @sql=replace(@sql,''"'','''''''')
        -- todo: for {sym}{spc} and {spc}{sym}
        end
    end -- options

return @sql
end -- fn__sql_normalize'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_normalize:

-- =========================================================== fn__sql_quotename
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_quotename',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080505
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_quotename') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_quotename') with nowait
        goto skip_fn__sql_quotename
        end
    if @ver>080505
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_quotename') with nowait
        goto skip_fn__sql_quotename
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_quotename') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_quotename'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_quotename]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080505\S.Zaglio: add bounds [ & ] if not exists
*/
create function fn__sql_quotename(
    @name sysname
)
returns sysname
as
begin
if left(@name,1)<>''['' and right(@name,1)<>'']'' set @name=quotename(@name)
return @name
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_quotename:

-- =============================================================== fn__sql_strip
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_strip',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100228
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_strip') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_strip') with nowait
        goto skip_fn__sql_strip
        end
    if @ver>100228
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_strip') with nowait
        goto skip_fn__sql_strip
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_strip') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_strip'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_strip]

begin try
exec dbo.sp_executesql @statement = N'/*
    l:see LICENSE file
    g:utility
    v:100228\s.zaglio: added strip of space and condition on comment
    v:091018\s.zaglio: remove comments from @sql
    t:
        declare @sql nvarchar(4000)  select @sql=''''
        create table #src (lno int identity(10,10),line nvarchar(4000))
        exec sp__script ''fn__sql_strip'',@out=''#src''
        select @sql=@sql+line+char(13)+char(10) from #src order by lno
        print @sql
        print ''------------------------------------------------------''
        print dbo.fn__sql_strip(@sql,0)
        print ''------------------------------------------------------''
        print dbo.fn__sql_strip(@sql,1)
        drop table #src
    t:sp__find ''fn__sql_strip''
*/
CREATE function [dbo].[fn__sql_strip](
    @sql nvarchar(4000),
    @comments bit=null
)
returns nvarchar(4000)
as
begin

declare @crlf nchar(2),@cr nchar(1),@lf nchar(1),@tab nchar(1)
select @crlf=crlf,@cr=cr,@lf=lf,@tab=tab from dbo.fn__sym()


select @sql=replace(@sql,@cr,'' '')
select @sql=replace(@sql,@lf,'' '')      -- newline
select @sql=replace(@sql,@tab,'' '')       -- tab
select @sql=ltrim(rtrim(@sql))
while (charindex(''  '',@sql)>0) select @sql=replace(@sql,''  '','' '')

if not @comments is null
    begin
    -- this bit strips out block comments. we need to strip them out before
    -- single line comments (like this one), because you could theoretically have
    -- a block comment like this:
    /* my comment
    -- is malformed */

    -- variables to hold the first and last character''s positions in the "next" block
    -- comment in the string
    declare @codeblockstart int, @codeblockend int
    set @codeblockstart = patindex(''%/*%'', @sql)

    -- loop as long as we still have comments to exorcise ;)
    while @codeblockstart > 0
    begin
    -- grab the last character in the code block by searching for the first incidence
    -- of */ (close comment) in the string.
    set @codeblockend = patindex(''%*/%'', @sql)

    -- "cut" out the comment by concatenating everything the the "left" and "right"
    -- of the comment
    set @sql = left(@sql, @codeblockstart - 1)
    + right(@sql, len(@sql) - (@codeblockend + 1))

    -- fetch the first character''s position in the next comment block, if there is one.
    set @codeblockstart = patindex(''%/*%'', @sql)
    end

    -- once code blocks are out, we can remove any lines commented by double dashes (like this one)
    -- variables to hold the first and last character''s position in the "next" code block.
    declare @doubledashstart int, @doubledashlineend int

    -- grab the first double-dash (if there is one)
    set @doubledashstart = patindex(''%--%'', @sql)
    while @doubledashstart > 0
    begin
    -- search for the first "new line" after the first "double dash"
    -- we can use nchar(13) and nchar(10) to find the new line.
    -- since patindex doesn''t have a "start" character, and we need to find
    -- the first new line after the double dash, we will search all characters
    -- after the double dash for the new line.
    set @doubledashlineend = patindex(''%'' + nchar(13) + nchar(10) + ''%'',
    right(@sql, len(@sql) - (@doubledashstart)))
    + @doubledashstart

    -- "cut" out the comment, as was done with the block comments.
    set @sql = left(@sql, @doubledashstart - 1) +
    right(@sql, len(@sql) - @doubledashlineend)

    -- check for the next incidence of a double dash, if there is one.
    set @doubledashstart = patindex(''%--%'', @sql)
    end

    -- return the uncommented string
    end -- comments

return @sql
end -- fn__sql_strip'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_strip:

-- ================================================================ fn__sql_trim
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_trim',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120412
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_trim') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_trim') with nowait
        goto skip_fn__sql_trim
        end
    if @ver>120412
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_trim') with nowait
        goto skip_fn__sql_trim
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_trim') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_trim'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_trim]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120412\s.zaglio: left and right trim lines to allow correct indent into code
    t:
        print dbo.fn__sql_trim(''
            /* test
                v:120412\s.zaglio:test
            */
            create proc
            -- test 2 right spaces
            '')
*/
CREATE function fn__sql_trim(@sql nvarchar(max))
returns nvarchar(max)
as
begin
declare @tmp nvarchar(max),@crlf nvarchar(2),@i int,@j int,@p int
declare @src table(lno int identity primary key,line nvarchar(4000))

select @crlf=crlf from fn__sym()

insert @src(line) select token
from dbo.fn__str_table(@sql,@crlf)

select top 1 @i=lno from @src where ltrim(rtrim(line))!='''' order by lno
select top 1 @j=lno from @src where ltrim(rtrim(line))!='''' order by lno desc

select @p=patindex(''%[^ ]%'',line) from @src where lno=@i

select @tmp=''''
select @tmp=@tmp+rtrim(substring(line,@p,4000))+@crlf
from @src
where lno between @i and @j

return @tmp
end -- fn__sql_trim'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_trim:

-- ========================================================= fn__sql_unquotename
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_unquotename',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090914
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_unquotename') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_unquotename') with nowait
        goto skip_fn__sql_unquotename
        end
    if @ver>090914
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_unquotename') with nowait
        goto skip_fn__sql_unquotename
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_unquotename') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_unquotename'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_unquotename]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090914\s.zaglio: revisited using parsename to manage composed names
    v:080505\S.Zaglio: remove bounds [ & ] if exists
    t:print dbo.fn__sql_unquotename(''[dbo].[test]'')
    t:print dbo.fn__sql_unquotename(''[test]'')
*/
CREATE function [dbo].[fn__sql_unquotename](
    @name sysname
)
returns sysname
as
begin
select @name=coalesce(parsename(@name,4)+''.'','''')+coalesce(parsename(@name,3)+''.'','''')+coalesce(parsename(@name,2)+''.'','''')+parsename(@name,1)
-- if left(@name,1)=''['' and right(@name,1)='']'' set @name=substring(@name,2,len(@name)-2)
return @name
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_unquotename:

-- ================================================================== fn__str_at
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_at',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131010
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_at') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_at') with nowait
        goto skip_fn__str_at
        end
    if @ver>131010
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_at') with nowait
        goto skip_fn__str_at
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_at') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_at'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_at]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,util_tkns
    v:131010\s.zaglio: a try of optimization
    v:111114\s.zaglio: simplyfied and added to grp util_tkns
    v:100919\s.zaglio: added management of sep '' ''
    v:091018\s.zaglio: replaced datalengh with len for problem with unicode
    v:090805\S.Zaglio: now on @pos=0 return null
    v:081212\S.Zaglio: now support @sep='' ''
    v:081204\S.Zaglio: removed bug limits 128 chars
    v:081130\S.Zaglio: replaced fn_str_at and optimized
    v:081110\S.Zaglio: expanded @seps to nvarchar(32)
    v:080909\S.Zaglio: added outbounds tests and -n as from right and return of varchar4000 nad null case
    v:080717\S.Zaglio: normalize position for fn_str_at
    t:
        -- due wide use of this, a good test is needed
        declare @tbl table(id int, s sysname,sep sysname,p int,v sql_Variant,r sql_variant)

        insert @tbl(id,s,sep,p,r) select  10,''a'',''|'',4,null
        insert @tbl(id,s,sep,p,r) select  20,''a|b'',''|'',-1,null
        insert @tbl(id,s,sep,p,r) select  22,''a|b'',''|'',-2,null
        insert @tbl(id,s,sep,p,r) select  24,''a|b'',''|'',-3,null
        insert @tbl(id,s,sep,p,r) select  30,'''',''|'',1,''''
        insert @tbl(id,s,sep,p,r) select  40,''a|b|c'',''|'',1,''a''
        insert @tbl(id,s,sep,p,r) select  50,''|b|c'',''|'',1,''''
        insert @tbl(id,s,sep,p,r) select  60,''a|b|c'',''|'',2,''b''
        insert @tbl(id,s,sep,p,r) select  70,''a|b|c'',''|'',3,''c''
        insert @tbl(id,s,sep,p,r) select  93,''a|b|c'',''|'',0,null
        insert @tbl(id,s,sep,p,r) select  96,''a b c'','''',3,''c''
        insert @tbl(id,s,sep,p,r) select 100,''abc de fg'','''',1,''abc''
        insert @tbl(id,s,sep,p,r) select 110,''abc de fg'','''',2,''de''
        insert @tbl(id,s,sep,p,r) select 120,''abc de fg'','''',3,''fg''
        insert @tbl(id,s,sep,p,r) select 130,''abc de fg'','''',4,null
        insert @tbl(id,s,sep,p,r) select 140,''abc   de fg   '',''  '',2,'' de fg''
        insert @tbl(id,s,sep,p,r) select 140,''abc   de fg   '','''',3,''''
        insert @tbl(id,s,sep,p,r) select 150,''   abc  de  fg   '','''',3,''''
        insert @tbl(id,s,sep,p,r) select 160,''   abc  de  fg   '',''abc'',4,null

        update @tbl set v=dbo.fn__str_at(s,sep,p)

        select *,case when v=r or v is null and r is null then ''ok'' else ''ko'' end rr
        from @tbl
*/
CREATE function [dbo].[fn__str_at](
    @data nvarchar(4000),
    @sep nvarchar(32),
    @pos int
    )
returns nvarchar(4000)
as
begin
declare @st nvarchar(4000)
declare @i int,@j int
declare @step int

if @data is null or @sep is null return null

--initialize
select @st = ''''
select @step = len(''.''+@sep+''.'')-2
if @sep = '''' and @step=0 select @sep = '' '',@step=1

select @data = @data + @sep , @j=1
select @i = charindex(@sep, @data)

/*
    this do not optimize nothing:
    - if @pos=1 return left(@data,@i-1) (same time)
    - use of fn__str_split or fn__str_parse that uses with (5-8 time slower)
*/

while (@i <> 0)
    begin
    select @pos=@pos-1
    if @pos=0 return substring(@data, @j, @i - @j)
    set @j=@i+@step
    set @i = charindex(@sep, @data,@j)
    end

return null
end -- fn__str_at'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_at:

-- ============================================================= fn__str_between
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_between',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130217
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_between') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_between') with nowait
        goto skip_fn__str_between
        end
    if @ver>130217
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_between') with nowait
        goto skip_fn__str_between
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_between') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_between'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_between]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130217\s.zaglio: added option close left
    v:111205\s.zaglio: a test when left token not found
    v:111114\s.zaglio: added space as separator and correct some bug
    v:110707\s.zaglio: a bug when @str = ''''
    v:100508\s.zaglio: return a pieec of string between limits
    t:print dbo.fn__str_between(''a:1,b:2,c:3'',''a:'','','',default) -->1
    t:print dbo.fn__str_between(''a:1,b:2,c:3'',''b:'','','',default) -->2
    t:print dbo.fn__str_between(''a:1,b:2,c:3'',''c:'','','',default) -->3
    t:print ''|''+dbo.fn__str_between(''a:1,b:2,c:3'',''x'',''y'',default)+''|'' -->''||''
    t:print isnull(dbo.fn__str_between(null,''c:'','','',default),''???'') -->???
    t:print ''|''+isnull(dbo.fn__str_between('''',''c:'','','',default),''???'')+''|'' -->''||''
    t:print dbo.fn__str_between(''test:with space'','':'','''',default) -->with
    t:print ''|''+dbo.fn__str_between(''||'',''|ss:'',''|'',default)+''|'' -->''||''
    t:print dbo.fn__str_between(''|ss:m0|'',''|ss:'',''|'',default) --> m0
    t:select dbo.fn__str_between(''c:\dir\file.ext'',''\'',''.'',[btw.close_right]) from enums
*/
CREATE function fn__str_between(
    @str nvarchar(4000),
    @from sysname,
    @to sysname,
    @opt smallint=null
    )
returns nvarchar(4000)
as
begin
declare @x1 int,@x2 int,@l int,@cr smallint,@lto int,@lfrom int

if @str is null or @from is null or @to is null return null
if @str='''' return @str

-- spaces tric
select @lto=len(''"''+@to+''"'')-2,@lfrom=len(''"''+@from+''"'')-2

if @opt is null
    begin
    select
        @x1=case
            when @from=''''
            then charindex('' '',@str)
            else charindex(@from,@str)
            end
    if @x1=0 return ''''
    select
        @x1=@x1+@lfrom,
        @x2=case
            when @to=''''
            then charindex('' '',@str,@x1)
            else charindex(@to,@str,@x1)
            end
    end
else
    begin
    select @cr=[btw.close_right] from enums
    if @opt=@cr
        begin
        select
            @x2=case
                when @to=''''
                then dbo.fn__charindex('' '',@str,-1)
                else dbo.fn__charindex(@to,@str,-1)
                end
        if @x2=0 return ''''
        select /*@x2=@x2-@lto,@x1=@x2*/ @x1=@x2-@lto
        while @x1>0 and substring(@str,@x1,@lfrom)!=@from
            select @x1=@x1-1
        if @x1>0 select @x1=@x1+@lfrom
        end
    end -- not default close_left

if @x1=@x2 return ''''
select
    @l=len(@str)

if @x2=0 select @x2=@l+1
return substring(@str,@x1,@x2-@x1)
end -- fn__str_between'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_between:

-- ================================================================= fn__str_chk
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_chk',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121004
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_chk') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_chk') with nowait
        goto skip_fn__str_chk
        end
    if @ver>121004
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_chk') with nowait
        goto skip_fn__str_chk
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_chk') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_chk'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_chk]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:121004\s.zaglio: renamed into fn__str_chk
    d:121004\s.zaglio: fn__chk_str
    v:110219\s.zaglio: check if a string contain only specified chars
    t:print dbo.fn__chk_str(''correct_me@email.it'',''a-z|0-9|_.@;'') -- ok
    t:print dbo.fn__chk_str(''correct_me@email.it,me@svr'',''a-z|0-9|_.;'') -- bad
*/
create function fn__str_chk(@str nvarchar(4000),@chars nvarchar(4000))
returns bit
as
begin
/*
declare @str nvarchar(4000),@chars nvarchar(4000)
select @str=''k@;k'',@chars=''a-z|@_''
-- print dbo.fn__str_quote(''a|b'',''|'')
*/
declare @tkn sysname,@c nchar(1),@i int,@l int,@ok bit
select  @chars=dbo.fn__str_unquote(@chars,''|''),
        @l=len(@str),@i=1

declare cs cursor local for
    select ''[''+token+'']''
    from dbo.fn__str_table(@chars,''|'')
while (@i<=@l)
    begin
    select @ok=0,@c=substring(@str,@i,1)
    open cs
    while 1=1
        begin
        fetch next from cs into @tkn
        if @@fetch_status!=0 break
        if @c like @tkn select @ok=1
        -- exec sp__printf ''c(%s) tkn(%s) ok(%d)'',@c,@tkn,@ok
        end
    close cs
    if @ok=0 return 0
    select @i=@i+1
    end

deallocate cs

return 1
end -- fn__chk_str'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_chk:

-- =============================================================== fn__str_count
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_count',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_count') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_count') with nowait
        goto skip_fn__str_count
        end
    if @ver>100919
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_count') with nowait
        goto skip_fn__str_count
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_count') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_count'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_count]

begin try
exec dbo.sp_executesql @statement = N'/*
    leave this
    l:see LICENSE file
    g:utility
    v:100919\s.zaglio: added sep '''' for '' ''
    v:100228\s.zaglio: optimized with use of charindex
    v:091018\s.zaglio: replaced datalenght(@sep) with len coz unicode problem
    v:081212\S.Zaglio: replaced len(@sep) with datalenght
    v:081130\S.Zaglio: optimization & corrected a bug
    v:081110\S.Zaglio: expanded @seps to nvarchar(32)
    v:080926\S.Zaglio: added null & '''' cases
    v:080721\S.Zaglio: count tokens
    t:print dbo.fn__str_count(''a|b|c'',''|'')  --> 3
    t:print dbo.fn__str_count(''a|b|'',''|'')   --> 3  (ex 2)
    t:print dbo.fn__str_count(''a'',''|'')      --> 1
    t:print dbo.fn__str_count('''',''|'')       --> 0
    t:print dbo.fn__str_count(null,''|'')     --> null
    t:print dbo.fn__str_count(''a b c d'','''')
*/
CREATE function [dbo].[fn__str_count](@tokens nvarchar(4000),@sep nvarchar(32)=''|'')
returns int
as
begin
    declare @i int set @i=0
    declare @n int set @n=0
    if @tokens is null return null
    if @tokens='''' return 0
    if @sep='''' select @tokens=replace(@tokens,'' '',''|''),@sep=''|''
    declare @k int set @k=len(@sep)
    declare @l int set @l=len(@tokens)
    select @i=charindex(@sep,@tokens)
    while @i>0
        begin
        select @n=@n+1
        select @i=charindex(@sep,@tokens,@i+@k)
        end -- while
/*  old:
    while @i<=@l begin
        if substring(@tokens,@i,@k)=@sep set @n=@n+1
        set @i=@i+1
    end -- while
*/
    return @n+1
end -- fn__str_count'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_count:

-- ================================================================= fn__str_cut
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_cut',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090910.1827
        begin
        if @aut!='G.Sorgente'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_cut') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_cut') with nowait
        goto skip_fn__str_cut
        end
    if @ver>090910.1827
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_cut') with nowait
        goto skip_fn__str_cut
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_cut') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_cut'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_cut]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    v:090910.1827\G.Sorgente: remove nchar from begin or end of the string
    --t: print dbo.fn__str_cut (''0002'',''0'',1)
    --t: print dbo.fn__str_cut (''2000'',''0'',1)
    --t: print dbo.fn__str_cut (''2000'',''0'',0)
    --t: print dbo.fn__str_cut (''2002'',''0'',1)
    --t: print dbo.fn__str_cut (''2002'',''0'',0)
    --t: print dbo.fn__str_cut (''    2        '',space(1),1)+''|''
    --t: print dbo.fn__str_cut (''    2        '',space(1),0)+''|''
*/
CREATE  function [dbo].[fn__str_cut] (
     @str  nvarchar(4000)
    ,@c    nvarchar(2)
    ,@left bit
) returns nvarchar(4000) as begin

if @c='''' or @c=space(1) set @c=N''Þ''
set @str = replace(@str,'' '',N''Þ'')
if @left is null      set @left=1
declare @ch nchar(2)  set @ch=''''
declare @i  int       if (@left=1) set @i=1 else set @i=len(@str)

while (1=1)
    begin

    set @ch=substring(@str,@i,1)
    if coalesce(@ch,'''')='''' goto ex

    if (@ch!=@c) goto ex
    if (@left=1)
    begin
    if (@i>=len(@str)) goto ex
    set @str=substring(@str,@i+1,len(@str))
    --set @i=@i+1
    end
    else
    begin
    if (@i<=1) goto ex
    set @str=substring(@str,1,@i-1)
    set @i=@i-1
    end -- (end -if)

    end -- (end - while)

ex:
set @str = replace(@str,N''Þ'','' '')
return @str

end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_cut:

-- ================================================================= fn__str_exp
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_exp',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091227
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_exp') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_exp') with nowait
        goto skip_fn__str_exp
        end
    if @ver>091227
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_exp') with nowait
        goto skip_fn__str_exp
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_exp') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_exp'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_exp]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091227\s.zaglio: added %idx% (not for multi)
    v:081110\S.Zaglio: expanded @seps to nvarchar(32)
    v:081011\S.Zaglio: added @@ marker for multiple replace
    v:080814\S.Zaglio: generalized separator
    v:080729\S.Zaglio: repeat expression with %% replacing it with tokens or repleace multiple @@
    t:print dbo.fn__str_exp(''dst.%%=src.%%'',''a|b|c'','', '') --> dst.a=src.a,dst.b=src.b,...
    t:print dbo.fn__str_exp(''dst.@@ and src.@@'',''a|b|c'',''|'')  --> dst.a and src.b  || MULTI
    t:print dbo.fn__str_exp(''%%=''''@@'''''',''a,b,c'','','') --> a=''@@'',b=''@@'',c=''@@''
    t:print dbo.fn__str_exp(''''''%%'''' [%idx%]'',''a,b,c'','','') --> ''a'' [1],''b'' [2],''c'' [3]
*/
CREATE function [dbo].[fn__str_exp](
    @expression nvarchar(4000),
    @tokens nvarchar(4000),
    @sep nvarchar(32)
)
returns nvarchar(4000)
as
begin
declare @r nvarchar(4000) select @r=''''
declare @n int,@i int,@k int,@l int
declare @multi bit select @multi=0
declare @stri sysname,@ri bit
if @sep is null return null
set @n=dbo.fn__str_count(@tokens,''%%'')
--return dbo.fn__printf(''%d'',@n,null,null,null,null,null,null,null,null,null)
select @ri=case when charindex(''%idx%'',@expression)>0 then 1 else 0 end
set @k=charindex(''%%'',@expression)
if @k=0 begin
    set @k=charindex(''@@'',@expression)
    if @k>0 begin set @multi=1 set @l=len(@expression) end else set @k=1
end
set @n=dbo.fn__str_count(@tokens,dbo.fn__trim(@sep))
set @i=1
while (@i<=@n and @k>0)
    begin
    if @multi=0
        begin
        if @ri=1
            begin
            select @stri=convert(sysname,@i)
            set @r=@r+replace(replace(@expression,''%idx%'',@stri),''%%'',
                dbo.fn__str_at(@tokens,dbo.fn__trim(@sep),@i))
            end
        else
            set @r=@r+replace(@expression,''%%'',dbo.fn__str_at(@tokens,dbo.fn__trim(@sep),@i))
        end
    else
        begin
        set @expression=substring(@expression,1,@k-1)+ dbo.fn__str_at(@tokens,dbo.fn__trim(@sep),@i)+substring(@expression,@k+2,@l)
        set @k=charindex(''@@'',@expression,1)
        end
    if @i<@n select @r=@r+@sep
    set @i=@i+1
end -- while
if @multi=0 return @r
return @expression
end -- fn__str_exp'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_exp:

-- ================================================================ fn__str_flow
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_flow',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120810.1200
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_flow') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_flow') with nowait
        goto skip_fn__str_flow
        end
    if @ver>120810.1200
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_flow') with nowait
        goto skip_fn__str_flow
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_flow') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_flow'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_flow]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:text,layout,grid
    v:120810.1200\s.zaglio: flow a horiz. list of items into a grid
    t:
        print dbo.fn__str_flow(''abcdefghi,abcdefghi,abcdefghi,abcdefghi,''+
                               ''abcdefghi,abcdefghi,abcdefghi,abcdefghi'',
                               '','',
                               default)
*/
CREATE function fn__str_flow(
    @list nvarchar(4000),
    @sep nvarchar(32),
    @opt sysname = null
    )
returns nvarchar(4000)
as
begin
declare
    @n int,@ret nvarchar(4000),@width smallint,@ll int,
    @crlf nvarchar(2)

select
    @width=80,
    @sep=isnull(@sep,''|''),
    @crlf=crlf
from fn__sym()

if not @opt is null
    begin
    select @opt=dbo.fn__str_quote(@opt,''|'')
    if charindex(''|132|'',@opt)>0 select @width=132
    end

select @n=max(len(token))+1 from dbo.fn__str_table(@list,@sep)
select @ll=(@width/@n*@n)+2
-- select * from dbo.fn__str_table(''a,b,c'','','')
select @ret =coalesce(@ret+@sep,'''')+left(token+replicate('' '',@n),@n)
            +case when (pos+1)%(@width/(@n+1))=0 then @crlf else ''  '' end
from dbo.fn__str_table(@list,@sep)

return @ret

end -- proc fn__str_flow'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_flow:

-- ================================================================ fn__str_guid
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_guid',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100511
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_guid') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_guid') with nowait
        goto skip_fn__str_guid
        end
    if @ver>100511
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_guid') with nowait
        goto skip_fn__str_guid
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_guid') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_guid'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_guid]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100511\s.zaglio: normalize a guid to become a table name
    t:print ''##''+dbo.fn__str_guid(newid())
*/
create function fn__str_guid(@gid uniqueidentifier)
returns sysname
as
begin
declare @s sysname
select @s=replace(convert(sysname,@gid),''-'','''')
return @s
end -- fn__str_guid'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_guid:

-- ================================================================== fn__str_in
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_in',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100306
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_in') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_in') with nowait
        goto skip_fn__str_in
        end
    if @ver>100306
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_in') with nowait
        goto skip_fn__str_in
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_in') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_in'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_in]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100306\s.zaglio: added match char by char if @sep=''''
    v:090610\S.Zaglio: match a seached list with a gived list
    t:print dbo.fn__str_in(''a,d'',''a,b,c,d'','','') --> 1
    t:print dbo.fn__str_in(''a,e'',''a,b,c,d'','','') --> 0
    t:print dbo.fn__str_in(null,''a,b,c,d'','','')  --> null
    t:print dbo.fn__str_in(''ab'',''abcd'','''') --> 1
    t:print dbo.fn__str_in(''ae'',''abcd'','''') --> 0
*/
CREATE function fn__str_in(
    @searched nvarchar(4000), @tokens nvarchar(4000),@sep nvarchar(32)=''|''
    )
returns bit
as
begin
declare @i int,@n int,@r bit
if @sep=''''
    begin
    select @n=len(@searched),@i=1
    while (@i<=@n)
        begin
        if charindex(substring(@searched,@i,1),@tokens)=0 return 0
        select @i=@i+1
        end
    select @r=1
    end
else
    begin
    select @n=count(*) from dbo.fn__str_table(@searched,@sep)
    select @i=count(*)
        from dbo.fn__str_table(@tokens,@sep) t
        inner join dbo.fn__str_table(@searched,@sep) s
        on t.token=s.token
    if @i=@n select @r=1 else select @r=0
    end
return @r
end -- fn__str_in'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_in:

-- ================================================================ fn__str_jaro
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_jaro',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100125
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_jaro') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_jaro') with nowait
        goto skip_fn__str_jaro
        end
    if @ver>100125
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_jaro') with nowait
        goto skip_fn__str_jaro
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_jaro') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_jaro'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_jaro]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:100125\s.zaglio: alternative to fn__str_distance based on jaro algho
    c:originally from http://www.sqlservercentral.com/articles/Fuzzy+Match/65702/
    t:print dbo.fn__str_jaro(''Peter'',''Pete'')    -- 0.933332
    t:print dbo.fn__str_jaro(''Peter'',''Peter'')
*/
CREATE function [dbo].[fn__str_jaro](@str1 varchar(4000), @str2 varchar(4000))
returns float as
begin
declare @jaro_distance float
declare @jaro_winkler_distance float
declare @prefixlength int
declare @prefixscalefactor float

-- used by calc of prefixlength
declare @i int
declare @n int
declare @foundit bit

-- used by calc of jaro
declare @common1 varchar(4000)
declare @common2 varchar(4000)
declare @common1_len int
declare @common2_len int
declare @s1_len int
declare @s2_len int
declare @match_window int
set @jaro_distance = 0

-- used by calc transposition
declare @transpose_cnt int
set @transpose_cnt = 0

set @prefixscalefactor = 0.1 --constant = .1

if @str1 is not null and @str2 is not null
    begin

    -- set @jaro_distance = dbo.fn__str_jaro_calc(@str1, @str2)
    set @match_window = 0
    set @s1_len = len(@str1)
    set @s2_len = len(@str2)
    -- set @match_window = dbo.fn__str_jaro_clmw(@s1_len, @s2_len)     -- calclatematchwindow
    set @match_window = case
        when @s1_len >= @s2_len
        then (@s1_len / 2) - 1
        else (@s2_len / 2) - 1
        end -- case

    set @common1 = dbo.fn__str_jaro_gcc(@str1, @str2, @match_window,@s1_len,@s2_len) -- getcommoncharacters
    set @common1_len = len(@common1)
    if @common1_len = 0 or @common1 is null goto exit_dist
    set @common2 = dbo.fn__str_jaro_gcc(@str2, @str1, @match_window,@s1_len,@s2_len)
    set @common2_len = len(@common2)
    if @common1_len <> @common2_len or @common2 is null goto exit_dist

    -- begin calc transposition
    -- set @transpose_cnt = dbo.fn__str_jaro_ctp](@common1_len, @common1, @common2) -- calctranspositions
    set @i = 0
    while @i < @s1_len
        begin
        if substring(@str1, @i+1, 1) <> substring(@str2, @i+1, 1)
            set @transpose_cnt = @transpose_cnt + 1
        set @i = @i + 1
        end -- while

    set @transpose_cnt  = @transpose_cnt / 2

    -- end calc transposition

    set @jaro_distance = @common1_len / (3.0 * @s1_len) + @common1_len / (3.0 * @s2_len) +
                        (@common1_len - @transpose_cnt) / (3.0 * @common1_len);

    exit_dist:


    -- set @prefixlength = dbo.fn__str_jaro_calc_pl(@str1, @str2)
    set @i = 0
    set @foundit = 0
    set @n = case
        when @prefixlength < @s1_len and @prefixlength < @s2_len
        then @prefixlength
        when @s1_len < @s2_len and @s1_len < @prefixlength
        then @s1_len
        else @s2_len
        end -- case
    while @i < @n and @foundit = 0
        begin
        if substring(@str1, @i+1, 1) <> substring(@str2, @i+1, 1)
            begin
            set @prefixlength = @i
            set @foundit = 1
            end
        set @i = @i + 1
        end -- while
    end -- if not str nulls
else
    set @prefixlength = 4

set @jaro_winkler_distance = @jaro_distance + ((@prefixlength * @prefixscalefactor) * (1.0 - @jaro_distance))
return @jaro_winkler_distance
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_jaro:

-- ============================================================ fn__str_jaro_gcc
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_jaro_gcc',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100125
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_jaro_gcc') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_jaro_gcc') with nowait
        goto skip_fn__str_jaro_gcc
        end
    if @ver>100125
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_jaro_gcc') with nowait
        goto skip_fn__str_jaro_gcc
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_jaro_gcc') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_jaro_gcc'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_jaro_gcc]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100125\s.zaglio: used by fn__str_jaro_gcc
    c:originally from www.sqlservercentral.com
    t:print dbo.fn__str_jaro(''Peter'',''Pete'')
*/
create function [dbo].fn__str_jaro_gcc(
    @str1 varchar(4000),
    @str2 varchar(4000),
    @match_window int,
    @s1_len int,
    @s2_len int
)
returns varchar(4000) as
begin
declare @commonchars varchar(4000)
declare @copy varchar(4000)
declare @char char(1)
declare @foundit bit

declare @i int
declare @j int
declare @j_max int

set @commonchars = ''''
set @copy = @str2

set @i = 1
while @i < (@s1_len + 1)
    begin
    set @char = substring(@str1, @i, 1)
    set @foundit = 0

    -- set j starting value
    if @i - @match_window > 1
        set @j = @i - @match_window
    else
        set @j = 1 -- set j stopping value

    if @i + @match_window <= @s2_len
        set @j_max = @i + @match_window
    else
        if @s2_len < @i + @match_window set @j_max = @s2_len

    while @j < (@j_max + 1) and @foundit = 0
        begin
        if substring(@copy, @j, 1) = @char
            begin
            set @foundit = 1
            set @commonchars = @commonchars + @char
            set @copy = stuff(@copy, @j, 1, ''#'')
            end
        set @j = @j + 1
        end -- while @j<...
    set @i = @i + 1
    end -- while @i<...

return @commonchars
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_jaro_gcc:

-- ================================================================ fn__str_mask
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_mask',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091127
        begin
        if @aut!='g.sorgente'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_mask') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_mask') with nowait
        goto skip_fn__str_mask
        end
    if @ver>091127
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_mask') with nowait
        goto skip_fn__str_mask
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_mask') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_mask'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_mask]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:091127\g.sorgente: mask some description
    t:
    select ''[''+dbo.fn__str_mask(''pippo'',''*'',1,4)+'']''
    select ''[''+dbo.fn__str_mask(''pipp'',''*'',1,2)+'']''
    select ''[''+dbo.fn__str_mask(''abracadabra'',''*'',2,null)+'']''
*/
CREATE function [dbo].[fn__str_mask] (
     @v nvarchar(4000)
    ,@m nvarchar(2)=''*'' -- mask char
    ,@s tinyint=0 -- (0=stuff in middle,1=left stuff,2=right_stuff)
    ,@n tinyint=0 -- (set static len stuff, set it to null to automatic calculate it)
)
returns nvarchar(4000)
as begin


    declare @r nvarchar(4000), @max int, @i int

    set @max  = coalesce(@n,convert(int,convert(float,len(@v))/2))
    set @m    = coalesce(@m,''*'')
    set @s    = coalesce(@s,0)
    set @v    = ltrim(rtrim(@v))
    set @i    = (len(@v)-@max)/2

    if coalesce(@v,'''')='''' begin set @r=@v goto ret end

    if @i<=0 and @s=0
    begin
        set @r=stuff(@v,1,len(@v),replicate(@m,len(@v)))
        goto ret
    end
    else
    begin
        select @r=case @s
        when 0 then stuff(@v,@i+1,@max,replicate(@m,@max))
        when 1 then stuff(@v,1,@max,replicate(@m,@max))
        when 2 then stuff(@v,(len(@v)-@max)+1,@max,replicate(@m,@max))
        end
    end

ret:
    return @r
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_mask:

-- ================================================================= fn__str_mix
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_mix',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090210
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_mix') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_mix') with nowait
        goto skip_fn__str_mix
        end
    if @ver>090210
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_mix') with nowait
        goto skip_fn__str_mix
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_mix') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_mix'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_mix]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090210\S.Zaglio: optimized
    v:080811\S.Zaglio: managed nulls
    v:080101\S.Zaglio: update on change of fn__at
    v:080730\S.Zaglio: mix words of two groups separated by pipe withour repeatitions
    t:print dbo.fn__str_mix(''a|b|c'',''a|c|d'',''|'') -->''a|b|c|d''
    t:print dbo.fn__str_mix(''d|b|c|a'',''a|c|b'',''|'') -->''d|b|c|a''
    t:print dbo.fn__str_mix('''',''a|c|d'',''|'') -->''a|b|d''
    t:print dbo.fn__str_mix(''a|b|c'','''',''|'') -->''a|b|c''
    c:very slow. Must be optimized
*/
CREATE  function fn__str_mix(@grp1 nvarchar(4000), @grp2 nvarchar(4000),@sep nvarchar(32)=''|'')
returns nvarchar(4000)
as
begin
declare @r nvarchar(4000) set @r=''''
declare @t table(pos int identity(1,1), token nvarchar(4000))
if @grp1!='''' and @grp2!=''''
    insert into @t select token from dbo.fn__str_table(@grp2,@sep)
    union select token from dbo.fn__str_table(@grp1,@sep)
else
    begin
    if @grp1='''' return @grp2 else return @grp1
    end
declare @n int,@i int
select @n=count(*) from @t
set @i=2
select @r=token from @t where pos=1
while (@i<=@n) begin select @r=@r+@sep+token from @t where pos=@i set @i=@i+1 end
return @r
/*
declare @n int
declare @m int
declare @i int
declare @token sysname
if @grp1 is null return @grp2
if @grp2 is null return @grp1

set @n=dbo.fn__str_count(@grp2,@sep)
set @m=dbo.fn__str_count(@grp2,@sep)
set @i=1
if @n>@m
    while (@i<=@n) begin
        set @token=dbo.fn__str_at(@grp1,@sep,@i)
        if dbo.fn__at(@token,@grp2,@sep)=0 begin
            if @grp2<>'''' set @grp2=@grp2+@sep
            set @grp2=@grp2+@token
        end -- if
        set @i=@i+1
    end -- while
else
    while (@i<=@m) begin
        set @token=dbo.fn__str_at(@grp2,@sep,@i)
        if dbo.fn__at(@token,@grp1,@sep)=0 begin
            if @grp1<>'''' set @grp1=@grp1+@sep
            set @grp1=@grp1+@token
        end -- if
        set @i=@i+1
    end -- while

if @n>@m return @grp2
return @grp1
*/
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_mix:

-- ================================================================= fn__str_pad
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_pad',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130523
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_pad') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_pad') with nowait
        goto skip_fn__str_pad
        end
    if @ver>130523
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_pad') with nowait
        goto skip_fn__str_pad
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_pad') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_pad'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_pad]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130523\s.zaglio: changed tag d of fn__pad to o
    v:091126\s.zaglio: renamed from fn__pad
    v:091027\s.zaglio: added @sep
    v:090611\s.Zaglio: align ntext and pad it
    o:130523\s.zaglio: fn__pad
    t:print dbo.fn__str_pad(''test'',10,null,null,null)+''|---''
    t:print dbo.fn__str_pad(1234,10,null,null,null)+''|---''
*/
CREATE function fn__str_pad(
    @src sql_variant,
    @width int,
    @sep nchar(1),
    @align tinyint,
    @decimals int
    )
returns nvarchar(4000)
as
begin
declare @s nvarchar(4000)
declare @n real, @num bit
select @s=convert(nvarchar(4000),@src,126)
if @align is null or @align=0
    if isnumeric(@s)=1 select @align=1,@sep=coalesce(@sep,''0'')
    else select @align=0,@sep=coalesce(@sep,'' '')
if @sep is null select @sep='' ''

if @align=0 select @s=left(@s+replicate(@sep,@width),@width)
if @align=1 select @s=right(replicate(@sep,@width)+@s,@width)
-- todo: align=1 rigth
-- todo: align=2 center
-- todo: decimal management
return @s
end -- fn__str_pad'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_pad:

-- =============================================================== fn__str_print
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_print',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100724
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_print') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_print') with nowait
        goto skip_fn__str_print
        end
    if @ver>100724
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_print') with nowait
        goto skip_fn__str_print
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_print') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_print'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_print]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100724\s.zaglio: added = and ''
    v:100228\s.zaglio: remove non ascii chars
    t:print dbo.fn__str_print(''abc:.d{f|5}6~4&-ò§sd='''''')
*/
CREATE function [dbo].[fn__str_print] (@s nvarchar(4000))
returns nvarchar(4000)
as
begin
/*
declare @i int select @i=32
while @i<128  begin
    print convert(sysname,@i)+'' ''+char(@i)
    select @i=@i+1
    end
*/
declare @i int,@p sysname
select @s=convert(varchar(4000),@s)
select @p=''%[^''''=a-zA-Z0-9!-~ -]%''
-- select @p=''%[^!-~ ]%''
select @i = patindex(@p, @s)
while @i > 0
begin
    select @s = replace(@s, substring(@s, @i, 1), '' '')
    select @i = patindex(@p, @s)
end
return @s
end -- fn__str_print'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_print:

-- =============================================================== fn__str_quote
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_quote',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120724
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_quote') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_quote') with nowait
        goto skip_fn__str_quote
        end
    if @ver>120724
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_quote') with nowait
        goto skip_fn__str_quote
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_quote') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_quote'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_quote]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120724\s.zaglio: modified to complete left or right quote
    v:111205\s.zaglio: specialized injection only for single&double quote
    v:090529\S.Zaglio: revision
    v:080505\S.Zaglio: close string into quotes if not just done and inject
    t: select dbo.fn__str_quote(''test'',''''''''),dbo.fn__str_quote(''test''''1'','''''''')
    t: select dbo.fn__str_quote(''''''test'''''',''''''''),dbo.fn__str_quote(''''''test''''''''1'','''''''')
    t: select dbo.fn__str_quote(''test'',''<>'')    --> <test>
    t: select dbo.fn__str_quote(null,''<>'') --> null
    t: select dbo.fn__str_quote(''test|'',''|''),dbo.fn__str_quote(''|test'',''|'')
*/
CREATE function [dbo].[fn__str_quote](
    @str nvarchar(4000),
    @quotes nvarchar(4)    -- single '', double " or couple ()
)
returns nvarchar(4000)
as
begin
if @str is null return null
declare @qo nvarchar(2),@qoe bit    -- Quote Open Exists
declare @qc nvarchar(2),@qce bit    -- Quote Close Exists
if len(@quotes)=1 select @qo=@quotes,@qc=@qo
if len(@quotes)=2 select @qo=left(@quotes,1),@qc=right(@quotes,1)
if left(@str,1) =@qo select @qoe=1 else select @qoe=0
if right(@str,1)=@qc select @qce=1 else select @qce=0
if @qo=@qc and @qo in ('''''''',''"'')
    select @str=replace(@str,@qo,@qo+@qo) -- inject

if @qoe=0 select @str=@qo+@str
if @qce=0 select @str=@str+@qc

return @str
end -- [fn__str_quote]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_quote:

-- ============================================================== fn__str_remove
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_remove',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=081110
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_remove') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_remove') with nowait
        goto skip_fn__str_remove
        end
    if @ver>081110
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_remove') with nowait
        goto skip_fn__str_remove
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_remove') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_remove'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_remove]

begin try
exec dbo.sp_executesql @statement = N'/*  LEAVE THIS
    l:see LICENSE file
    g:utility
    v:081110\S.Zaglio: expanded @seps to nvarchar(32)
    v:080730\S.Zaglio: remove words from group
    t:print dbo.fn__str_remove(''a|b|c'',''a'',''|'') -->b|c
    t:print dbo.fn__str_remove(''a|b|c'',''a|c'',''|'') -->b
    t:print dbo.fn__str_remove(''a|b|c'',''d'',''|'') -->a|b|c
    t:print isnull(dbo.fn__str_remove(''a|b|c'',''a|b|c'',''|''),''(null)'') -->''''
*/
CREATE function fn__str_remove(@tokens nvarchar(4000), @remove nvarchar(4000),@sep nvarchar(32)=''|'')
returns nvarchar(4000)
as
begin
/*
declare @tokens nvarchar(4000)
declare @remove nvarchar(4000)
declare @sep nvarchar(8) set @sep=''|''
set @tokens=''a|b|c''
set @remove=''a''
*/
declare @n int
declare @r nvarchar(4000) set @r=''''
declare @i int
declare @k int
declare @token sysname
set @n=dbo.fn__str_count(@tokens,@sep)

set @i=1

while (@i<=@n) begin
    set @token=dbo.fn__str_at(@tokens,@sep,@i)
    set @k=dbo.fn__at(@token,@remove,@sep)
    if @k=0 begin
        if @r<>'''' set @r=@r+@sep
        set @r=@r+@token
    end -- if
    set @i=@i+1
end -- while

return @r
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_remove:

-- ============================================================= fn__str_replace
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_replace',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120117
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_replace') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_replace') with nowait
        goto skip_fn__str_replace
        end
    if @ver>120117
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_replace') with nowait
        goto skip_fn__str_replace
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_replace') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_replace'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_replace]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120117\s.zaglio: func version of sp__str_replace
    t:
        select dbo.fn__str_replace(
            ''test %r% and %p% and %c%'',
            ''%r%|%c%|%p%'',
            1,getdate(),''a'',
            default,default,default,default,default,default,default,default
            )
*/
create function fn__str_replace(
    @sentence nvarchar(4000)=null,
    @tokens nvarchar(4000)=null,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null,
    @p5 sql_variant=null,
    @p6 sql_variant=null,
    @p7 sql_variant=null,
    @p8 sql_variant=null,
    @p9 sql_variant=null,
    @p0 sql_variant=null,
    @sep nvarchar(32)
    )
returns nvarchar(4000)
as
begin
declare
    @tk1 sysname,@tk2 sysname,@tk3 sysname,@tk4 sysname,@tk5 sysname,
    @tk6 sysname,@tk7 sysname,@tk8 sysname,@tk9 sysname,@tk0 sysname

select @sep=isnull(@sep,''|'')

select
    @tk1=case when pos=1  then token else @tk1 end,
    @tk2=case when pos=2  then token else @tk2 end,
    @tk3=case when pos=3  then token else @tk3 end,
    @tk4=case when pos=4  then token else @tk4 end,
    @tk5=case when pos=5  then token else @tk5 end,
    @tk6=case when pos=6  then token else @tk6 end,
    @tk7=case when pos=7  then token else @tk7 end,
    @tk8=case when pos=8  then token else @tk8 end,
    @tk9=case when pos=9  then token else @tk9 end,
    @tk0=case when pos=10 then token else @tk0 end
from dbo.fn__str_table(@tokens,@sep) t

/*
return   isnull(@tk1,''tk1'')+''|''+isnull(@tk2,''tk2'')+''|''
        +isnull(@tk3,''tk3'')+''|''+isnull(@tk4,''tk4'')+''|''
        +isnull(@tk5,''tk5'')+''|''+isnull(@tk6,''tk6'')+''|''
        +isnull(@tk7,''tk7'')+''|''+isnull(@tk8,''tk8'')+''|''
        +isnull(@tk9,''tk9'')+''|''+isnull(@tk0,''tk0'')+''|''
*/

if not @tk1 is null select @sentence=replace(@sentence,@tk1,convert(nvarchar(4000),@p1))
if not @tk2 is null select @sentence=replace(@sentence,@tk2,convert(nvarchar(4000),@p2))
if not @tk3 is null select @sentence=replace(@sentence,@tk3,convert(nvarchar(4000),@p3))
if not @tk4 is null select @sentence=replace(@sentence,@tk4,convert(nvarchar(4000),@p4))
if not @tk5 is null select @sentence=replace(@sentence,@tk5,convert(nvarchar(4000),@p5))
if not @tk6 is null select @sentence=replace(@sentence,@tk6,convert(nvarchar(4000),@p6))
if not @tk7 is null select @sentence=replace(@sentence,@tk7,convert(nvarchar(4000),@p7))
if not @tk8 is null select @sentence=replace(@sentence,@tk8,convert(nvarchar(4000),@p8))
if not @tk9 is null select @sentence=replace(@sentence,@tk9,convert(nvarchar(4000),@p9))
if not @tk0 is null select @sentence=replace(@sentence,@tk0,convert(nvarchar(4000),@p0))

return @sentence
end -- fn__str_replace'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_replace:

-- ============================================================ fn__str_scramble
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_scramble',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110527
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_scramble') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_scramble') with nowait
        goto skip_fn__str_scramble
        end
    if @ver>110527
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_scramble') with nowait
        goto skip_fn__str_scramble
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_scramble') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_scramble'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_scramble]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110527\S.Zaglio: a remake (the older will be a str generator)
    v:091018\s.zaglio: added scramble for numbers or chars only and a bug on len
    v:081218\S.Zaglio: scramble data
    c:remake of previous fun with same name
    t:
        select  dbo.fn__str_scramble(''stefano zaglio'',
                                     rand(checksum(newid()))
                                    ) as [stefano zaglio],
                dbo.fn__str_scramble(''stefano zaglio'',
                                     rand(checksum(newid()))
                                    ) as [stefano zaglio],
                dbo.fn__str_scramble(''zaglio stefano'',
                                     rand(checksum(newid()))
                                    ) as [zaglio stefano],
                dbo.fn__str_scramble(''zaglio stefania'',
                                     rand(checksum(newid()))
                                    ) as [zaglio stefania]
*/
CREATE function fn__str_scramble(@data nvarchar(4000),@rand real)
returns nvarchar(4000)
as
begin
-- declare @data sysname select @data=''stefano zaglio''
declare
    @len int,
    @i int,  -- index
    @p int,  -- random position,
    @n int,
    @c nvarchar(1),
    @s int
-- declare @len int select @len=10 -- declare @len int

declare @rnd table(id int identity,r int)
/*
    select top 32
        ''union all select ''+convert(sysname,(RAND(CHECKSUM(NEWID()))))+''*@len+1''
    from sysobjects

    SELECT CAST(CAST(newid() AS binary(1)) AS int)/256.0

    select RAND(CHECKSUM(NEWID()))

    DECLARE @maxRandomValue TINYINT, @minRandomValue TINYINT
    select @maxRandomValue = 100,  @minRandomValue = 0
    SELECT CAST(((@maxRandomValue + 1) - @minRandomValue)
        * RAND(CHECKSUM(NEWID())) + @minRandomValue AS TINYINT) AS ''randomNumber''
*/
select @len=len(@data),@i=1,@n=32,@s=20*@rand

insert @rnd(r)
-- declare @len int select @len=10
          select 0.648147*@len+1
union all select 0.49276*@len+1
union all select 0.760752*@len+1
union all select 0.92745*@len+1
union all select 0.550134*@len+1
union all select 0.327785*@len+1
union all select 0.223983*@len+1
union all select 0.602884*@len+1
union all select 0.611201*@len+1
union all select 0.920175*@len+1
union all select 0.626131*@len+1
union all select 0.666639*@len+1
union all select 0.490854*@len+1
union all select 0.714348*@len+1
union all select 0.931448*@len+1
union all select 0.202067*@len+1
union all select 0.136386*@len+1
union all select 0.400647*@len+1
union all select 0.33078*@len+1
union all select 0.169345*@len+1
union all select 0.227007*@len+1
union all select 0.293043*@len+1
union all select 0.916565*@len+1
union all select 0.816953*@len+1
union all select 0.923834*@len+1
union all select 0.873596*@len+1
union all select 0.379763*@len+1
union all select 0.857231*@len+1
union all select 0.468347*@len+1
union all select 0.351739*@len+1
union all select 0.445449*@len+1
union all select 0.860574*@len+1
-- select * from @rnd

while (@i<=@len)
    begin
    select
        @c=substring(@data,@i,1),
        @p=isnull((select r from @rnd where id=(@i+@s)%@n),1),
        @data=stuff(@data,@i,1,substring(@data,@p,1)),
        @data=stuff(@data,@p,1,@c),
        @i=@i+1
    -- select @c c,@p p,@data data,@i i,@n n
    end
return substring(''abcdefghijklmnopqrstuvwxyz'',@s,1)+@data
end -- fn__str_scramble'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_scramble:

-- ============================================================ fn__str_simplify
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_simplify',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090624
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_simplify') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_simplify') with nowait
        goto skip_fn__str_simplify
        end
    if @ver>090624
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_simplify') with nowait
        goto skip_fn__str_simplify
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_simplify') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_simplify'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_simplify]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090624\s.zaglio: default left/right trim
    v:081016\S.Zaglio: remove initila crlf and spaces
    v:080814\S.Zaglio: added replace of CRLF+SPACE to CRLF and corrected a bug nchar(13)->char(10)
    v:080812\S.Zaglio: added replace of SPACE+CRLF to CRLF
    v:080717\S.Zaglio: added @trim  (1=left,2=left,0=left&right)
    v:080401\S.Zaglio: remove double spaces, crlf, tabs
    t:print ''|''+dbo.fn__str_simplify(''  test  2space   3space   '',default)+''|''
    t:print ''|''+dbo.fn__str_simplify(''  test  2space   3space   '',1)+''|''
*/
CREATE function fn__str_simplify(@str nvarchar(4000),@trim smallint=0)
returns nvarchar(4000)
as
begin
declare @crlf nchar(2)
set @crlf=char(13)+char(10)
if @trim=1 set @str=ltrim(@str)
if @trim=2 set @str=rtrim(@str)
if @trim=0 set @str=ltrim(rtrim(@str))
while left(@str,2)=@crlf set @str=substring(@str,3,len(@str))
while isnull(charindex(nchar(9),        @str),0)>0 set @str=replace(@str,char(9),        '' '')
while isnull(charindex(@crlf+@crlf,    @str),0)>0 set @str=replace(@str,@crlf+@crlf,    @crlf)
while isnull(charindex(''   '',          @str),0)>0 set @str=replace(@str,''   '',          '' '')
while isnull(charindex(''  '',           @str),0)>0 set @str=replace(@str,''  '',           '' '')
while isnull(charindex('' ''+@crlf,      @str),0)>0 set @str=replace(@str,'' ''+@crlf,      @crlf)
while isnull(charindex(@crlf+'' '',      @str),0)>0 set @str=replace(@str,@crlf+'' '',      @crlf)
while isnull(charindex('' ''+@crlf+'' '',  @str),0)>0 set @str=replace(@str,'' ''+@crlf+'' '',  @crlf)
return @str
end -- fn__str_simplify'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_simplify:

-- ============================================================= fn__str_unquote
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_unquote',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120914
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_unquote') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_unquote') with nowait
        goto skip_fn__str_unquote
        end
    if @ver>120914
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_unquote') with nowait
        goto skip_fn__str_unquote
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_unquote') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_unquote'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_unquote]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    v:120914\s.zaglio: a small bug if len(@str)=1
    v:100112\S.Zaglio: unquote with specification
    t:print dbo.fn__str_unquote(''[test]'',''[]'')
*/
CREATE function [dbo].[fn__str_unquote](
    @str nvarchar(4000),
    @quotes nvarchar(4)    -- single '', double " or couple ()
)
returns nvarchar(4000)
as
begin
if @str is null return null
declare @qo nvarchar(2)
declare @qc nvarchar(2)
declare @l int,@lr int,@ll int
if len(@str)<2 return @str
select @l=len(@quotes)
if @l=1 begin set @qo=@quotes set @qc=@qo end
if @l=2 begin set @qo=left(@quotes,1) set @qc=right(@quotes,1) end
select @lr=len(@qo),@ll=len(@qc),@l=len(@str)
if left(@str,1)=@qo and right(@str,1)=@qc begin
    select @str=substring(@str,@lr+1,@l-@lr-@ll)
    select @str=replace(@str,@qo+@qo,@qo) -- un-inject
    end
return @str
end -- [fn__str_unquote]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_unquote:

-- =================================================================== fn__subst
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__subst',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080430
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__subst') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__subst') with nowait
        goto skip_fn__subst
        end
    if @ver>080430
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__subst') with nowait
        goto skip_fn__subst
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__subst') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__subst'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__subst]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080430\S.Zaglio: @action=1->left part of string from @from
    t: print dbo.fn__subst(default,''CREATE_SYNC_STRUCTURE:[gamon.seldom.it].ramsesbg'','':'',default)
*/
CREATE    function fn__subst(@action tinyint=1,@str nvarchar(4000),@from nvarchar(8),@dummy nvarchar(4000)=null)
returns nvarchar(4000)
as
begin
return substring(@str,isnull(charindex(@from,@str)+len(@from),len(@str)),len(@str))
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__subst:

-- ====================================================================== fn__t9
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__t9',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090609
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__t9') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__t9') with nowait
        goto skip_fn__t9
        end
    if @ver>090609
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__t9') with nowait
        goto skip_fn__t9
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__t9') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__t9'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__t9]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090609\S.Zaglio: adapted to sqlce
    v:061226\S.Zaglio: get like expression on keys pressed
    t:print dbo.fn__t9(''cod'',''.1234567890'')
*/
CREATE function [dbo].[fn__t9](@fld sysname, @t9 nvarchar(32))
returns nvarchar(4000)
as
begin
/*
    select top 5 * from table where 1=1 -- search for t9:23
    and (cod like ''a%'' or cod like ''b%'' or cod like ''c%'' or cod like ''2%'')
    and (cod like ''_d%'' or cod like ''_e%'' or cod like ''_f%'' or cod like ''_3%'')
    and ...
*/
declare @r nvarchar(4000) set @r=''''
declare @c nchar(1)
declare @s sysname,@chars sysname
declare @i int, @j int , @l int
select @i=1,@l=len(@t9)
while (@i<=@l)
    begin
    select @r=@r+case @i when @l then '' and (('' else '' and ('' end
    select @c=substring(@t9,@i,1)
    select @j=@i-1,@s=''''
    while (@j>0) select @s=@s+N''_'',@j=@j-1
    set @chars =
        case @c
        when ''0'' then ''0''
        when ''1'' then ''1''
        when ''2'' then ''cba2''
        when ''3'' then ''fed3''
        when ''4'' then ''ihg4''
        when ''5'' then ''lkj5''
        when ''6'' then ''onm6''
        when ''7'' then ''srqp7''
        when ''8'' then ''vut8''
        when ''9'' then ''zyxw9''
        else ''_''
        end -- case
    select @j=len(@chars)
    while (@j>0)
        begin
        select @r=@r+@fld+'' like ''''''+@s+substring(@chars,@j,1)+''%'''''',@j=@j-1
        if @j>0 select @r=@r+'' or ''
        end
    set @r=@r+'')''
    set @i=@i+1
    end -- while i<len(t9)
set @r=@r+'')''
return @r
end -- function t9'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__t9:

-- ================================================================== fn__t9_cmp
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__t9_cmp',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090430
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__t9_cmp') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__t9_cmp') with nowait
        goto skip_fn__t9_cmp
        end
    if @ver>090430
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__t9_cmp') with nowait
        goto skip_fn__t9_cmp
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__t9_cmp') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__t9_cmp'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__t9_cmp]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090430\s.zaglio
*/
CREATE function fn__t9_cmp(@t9_src int, @len tinyint, @t9_key int)
returns bit
as
begin
if convert(int,left(convert(varchar(12),@t9_key),@len))=@t9_src return 1
return 0
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__t9_cmp:

-- ================================================================== fn__t9_key
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__t9_key',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090430
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__t9_key') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__t9_key') with nowait
        goto skip_fn__t9_key
        end
    if @ver>090430
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__t9_key') with nowait
        goto skip_fn__t9_key
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__t9_key') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__t9_key'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__t9_key]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090430\S.Zaglio: revision: added @max_len and set result to bigint
*/
CREATE function [dbo].[fn__t9_key](@string nvarchar(4000),@max_len tinyint=9)
returns bigint
as
begin
declare @c nvarchar(1)
declare @i int set @i=1
declare @r bigint set @r=0
if @max_len>19 set @max_len=19
while (@i<=@max_len and @i<=len(@string)) begin
    set @r=@r*10
    set @c=substring(@string,@i,1)
    set @i=@i+1
    if @c=''0'' and @i=1 begin set @r=-@r continue end
    if @c=''1'' begin set @r=@r+1 continue end
    if @c like ''[2abc]'' begin set @r=@r+2 continue end
    if @c like ''[3def]'' begin set @r=@r+3 continue end
    if @c like ''[4ghi]'' begin set @r=@r+4 continue end
    if @c like ''[5jkl]'' begin set @r=@r+5 continue end
    if @c like ''[6mno]'' begin set @r=@r+6 continue end
    if @c like ''[7pqrs]'' begin set @r=@r+7 continue end
    if @c like ''[8tuv]''  begin set @r=@r+8 continue end
    if @c like ''[9wxyz]'' begin set @r=@r+9 continue end
end -- while
return @r
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__t9_key:

-- ================================================================= fn__t9_vkey
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__t9_vkey',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090430
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__t9_vkey') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__t9_vkey') with nowait
        goto skip_fn__t9_vkey
        end
    if @ver>090430
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__t9_vkey') with nowait
        goto skip_fn__t9_vkey
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__t9_vkey') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__t9_vkey'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__t9_vkey]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090430/S.Zaglio: revision: added @max_len and set result to bigint
*/
create function [dbo].[fn__t9_vkey](@string nvarchar(4000),@max_len tinyint=9)
returns nvarchar(32)
as
begin
declare @c nvarchar(1)
declare @i int set @i=1
declare @r nvarchar(32) set @r=''''
if @max_len>19 set @max_len=19
while (@i<=@max_len and @i<=len(@string)) begin
    set @c=substring(@string,@i,1)
    set @i=@i+1
    if @c like ''[1*]'' begin set @r=@r+''1'' continue end
    if @c like ''[2abc]'' begin set @r=@r+''2'' continue end
    if @c like ''[3def]'' begin set @r=@r+''3'' continue end
    if @c like ''[4ghi]'' begin set @r=@r+''4'' continue end
    if @c like ''[5jkl]'' begin set @r=@r+''5'' continue end
    if @c like ''[6mno]'' begin set @r=@r+''6'' continue end
    if @c like ''[7pqrs]'' begin set @r=@r+''7'' continue end
    if @c like ''[8tuv]''  begin set @r=@r+''8'' continue end
    if @c like ''[9wxyz]'' begin set @r=@r+''9'' continue end
    set @r=@r+''0'' -- normalize for everything not recodnized
end -- while
return @r
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__t9_vkey:

-- ============================================================== fn__taxcode_it
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__taxcode_it',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130830
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__taxcode_it') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__taxcode_it') with nowait
        goto skip_fn__taxcode_it
        end
    if @ver>130830
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__taxcode_it') with nowait
        goto skip_fn__taxcode_it
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__taxcode_it') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__taxcode_it'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__taxcode_it]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    d:130830\s.zaglio: fn__taxcode
    v:130830\s.zaglio: renamed into fn__taxcode_it
    v:101209\d.averoldi: corrected some bug
    v:101208\s.zaglio: calculate italian TAX code (only for check, not really legal)
    c:from http://microsoft-it.confusenet.com/showthread.php?t=16620
    t:select dbo.fn__taxcode(''Stefano'', ''Zaglio'', 1, ''19720201'', ''X000'') --> ZGLSFN72B01X000R
    t:select dbo.fn__taxcode(''Davide'', ''Averoldi'', 1, '''', ''X000'') -->    --> VRLDVD00A01X000W
*/
create function fn__taxcode_it(
    @first_name varchar(255),
    @last_name varchar(255),
    @male bit,                  -- 1=male 0=female
    @birth_day datetime,        -- yyyymmdd
    @region_code char(4))       -- http://it.wikipedia.org/wiki/Codice_catastale
returns varchar(16) as begin

declare @tax varchar(16)
set @tax=''''

/* -- TEST --
declare @d datetime
set @d=getdate()
select dbo.fn__GetCf(''paolo'', ''Bigongiari'', 1, @d, ''c124'')
*/


/*
Cognome (3 lettere)
Vengono prese le consonanti del cognome (o dei cognomi, se ve ne è più
di uno) nel loro ordine: solo se sono insufficienti, si prelevano anche
le vocali, sempre nel loro ordine: comunque, le vocali vengono riportate
dopo le consonanti. Nel caso in cui un cognome abbia meno di tre
lettere, la parte di codice viene completata aggiungendo la lettera X
(es.: Fo -> FOX). Per le donne, viene preso in considerazione il solo
cognome da nubile.
*/
set @last_name=replace(@last_name, '' '', '''')
set @last_name=replace(@last_name, '''''''', '''')
if @last_name like ''%[^a-z]%'' return null
if @last_name='''' return null;

declare @T table(L char(1) not null, R varchar(255) not null, N int
primary key, N2 int not null, Vocale as case when L in(''a'', ''e'', ''i'',
''o'', ''u'') then 1 else 0 end )
insert into @T
select L = left(@last_name,1), R = stuff(@last_name, 1, 1, ''''), N=1, 0
while (select max(N) from @T) < len(@last_name)
begin
insert into @T
select top 1 L = left(R,1), R = stuff(R, 1, 1, ''''), N=N+1, 0
from @T
order by N desc
if @@rowcount=0 break
end;

select top 3 @tax = @tax + L
from @T x
order by Vocale, N

set @tax = left(@tax + ''xx'', 3);

/*
Nome (3 lettere)
Vengono prese le consonanti del nome (o dei nomi, se ve ne è più di
uno) in questo modo: se il nome contiene quattro o più consonanti, si
scelgono la prima, la terza e la quarta, altrimenti le prime tre in
ordine. Solo se il nome non ha consonanti a sufficienza, si prendono
anche le vocali: comunque, le vocali vengono riportate dopo le
consonanti. Nel caso in cui il nome abbia meno di tre lettere, la parte
di codice viene completata aggiungendo la lettera X. Un caso estremo si
incontra in alcuni soggetti provenienti dall''India nel passaporto dei
quali è riportata una sola parola al posto del cognome e del nome. Si
userà allora quella parola per generare le prime tre lettere del codice
e, non esistendo il nome, la seconda terzina di lettere del codice sarà
XXX.
*/

set @first_name=replace(@first_name, '' '', '''')
set @first_name=replace(@first_name, '''''''', '''')
if @first_name like ''%[^a-z]%'' return null;
delete @T;
if @first_name>''''
begin
insert into @T
select L = left(@first_name,1), R = stuff(@first_name, 1, 1, ''''), N=1, 0
while (select max(N) from @T) < len(@first_name)
begin
insert into @T
select top 1 L = left(R,1), R = stuff(R, 1, 1, ''''), N=N+1, 0
from @T
order by N desc
if @@rowcount=0 break
end;
end

declare @Cs int
select @Cs=count(*) from @T where Vocale=0

update @T
set N2=case t.Vocale when 0 then t.N-K else t.N-K + @Cs end
from @T t
inner join(
select t1.N, K=sum(isnull(t2.Vocale,0))
from @T t1
left outer join @T t2
on t2.N<t1.N
group by t1.N
) x
on x.N=t.N



select top 3 @tax = @tax + L
from @T
where @Cs<=3 or N2!=2
order by N2, N

set @tax = left(@tax + ''xxx'', 6)

/*
Data di nascita e sesso (5 caratteri alfanumerici)
Anno di nascita (2 cifre): si prendono le ultime due cifre dell''anno di
nascita;
Mese di nascita (1 lettera): ad ogni mese dell''anno viene associata una
lettera in base a questa tabella:
Lettera Mese Lettera Mese Lettera Mese
A gennaio E maggio P settembre
B febbraio H giugno R ottobre
C marzo L luglio S novembre
D aprile M agosto T dicembre
Giorno di nascita e sesso (2 cifre): si prendono le due cifre del
giorno di nascita (se è compreso tra 1 e 9 si pone uno zero come prima
cifra); per i soggetti di sesso femminile a tale cifra va sommato il
numero 40.
*/

set @tax = @tax + right(convert(varchar,year(@birth_day)),2) +
case month(@birth_day) when 1 then ''A'' when 2 then ''B'' when 3 then
''C'' when 4 then ''D'' when 5 then ''E'' when 6 then ''H'' when 7 then ''L'' when
8 then ''M'' when 9 then ''P'' when 10 then ''R'' when 11 then ''S'' when 12
then ''T'' end +
right(''0'' + convert(varchar, day(@birth_day) + case @male when 1
then 0 else 40 end),2)

/*
Comune di nascita (4 caratteri alfanumerici)
Per questa parte di codice viene utilizzato il codice catastale del
comune di nascita, ossia una codifica predisposta dalla Direzione
Generale del Catasto, composta da una lettera e 3 cifre numeriche. Per i
nati al di fuori del territorio italiano si considera lo Stato estero di
nascita; in tal caso la sigla inizia con la lettera Z, seguita dal
numero identificativo della nazione.
*/

if @region_code is null return null
if @region_code not like ''[a-z][0-9][0-9][0-9]'' return null
set @tax = @tax + @region_code;

/*
Codice di controllo (1 lettera)
A partire dai 15 caratteri alfanumerici ricavati in precedenza, si
determina il codice di controllo in base ad un particolare algoritmo,
che opera in questo modo: si mettono da una parte i caratteri
alfanumerici che si trovano in posizione dispari (il 1º, il 3º ecc.) e
da un''altra quelli che si trovano in posizione pari (il 2º, il 4º ecc.).
Fatto questo, i caratteri vengono convertiti in valori numerici
rispettando le seguenti tabelle:
<CUT>
*/

delete @T;
insert into @T
select L = left(@tax,1), R = stuff(@tax, 1, 1, ''''), N=1, 0
while (select max(N) from @T) < len(@tax)
begin
insert into @T
select top 1 L = left(R,1), R = stuff(R, 1, 1, ''''), N=N+1, 0
from @T
order by N desc
if @@rowcount=0 break
end;

select @tax = @tax + char(97+k)
from (
select k=sum(case N%2 when 1 then
case L when ''0'' then 1 when ''9'' then 21 when ''I'' then 19 when
''R'' then 8
when ''1'' then 0 when ''A'' then 1 when ''J'' then 21 when ''S''
then 12
when ''2'' then 5 when ''B'' then 0 when ''K'' then 2 when ''T''
then 14
when ''3'' then 7 when ''C'' then 5 when ''L'' then 4 when ''U''
then 16
when ''4'' then 9 when ''D'' then 7 when ''M'' then 18 when ''V''
then 10
when ''5'' then 13 when ''E'' then 9 when ''N'' then 20 when ''W''
then 22
when ''6'' then 15 when ''F'' then 13 when ''O'' then 11 when ''X''
then 25
when ''7'' then 17 when ''G'' then 15 when ''P'' then 3 when ''Y''
then 24
when ''8'' then 19 when ''H'' then 17 when ''Q'' then 6 when ''Z''
then 23 end
else
case L when ''0'' then 0 when ''9'' then 9 when ''I'' then 8 when
''R'' then 17
when ''1'' then 1 when ''A'' then 0 when ''J'' then 9 when ''S'' then 18
when ''2'' then 2 when ''B'' then 1 when ''K'' then 10 when ''T''
then 19
when ''3'' then 3 when ''C'' then 2 when ''L'' then 11 when ''U''
then 20
when ''4'' then 4 when ''D'' then 3 when ''M'' then 12 when ''V''
then 21
when ''5'' then 5 when ''E'' then 4 when ''N'' then 13 when ''W''
then 22
when ''6'' then 6 when ''F'' then 5 when ''O'' then 14 when ''X''
then 23
when ''7'' then 7 when ''G'' then 6 when ''P'' then 15 when ''Y''
then 24
when ''8'' then 8 when ''H'' then 7 when ''Q'' then 16 when ''Z''
then 25 end
end)%26
from @T x
)s

return upper(@tax)
end -- fn__taxcode'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__taxcode_it:

-- ==================================================================== fn__time
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__time',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=111215
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__time') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__time') with nowait
        goto skip_fn__time
        end
    if @ver>111215
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__time') with nowait
        goto skip_fn__time
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__time') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__time'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__time]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:time
    v:111215\s.zaglio: when hour is ss.000, 126 strip to ss
    v:111004\s.zaglio: return a float with time of datetime. decimals as ms
    t:select dbo.fn__time(getdate())
*/
CREATE function fn__time(@dt datetime)
returns float
as
begin
declare @f float,@s varchar(32)
select @s=right(convert(varchar(32),@dt,114),12)
select @f=convert(float,replace(left(@s,8),'':'','''')+''.''+right(@s,3))
return @f
end -- fn__time'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__time:

-- =============================================================== fn__token_sql
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__token_sql',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=111128
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__token_sql') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__token_sql') with nowait
        goto skip_fn__token_sql
        end
    if @ver>111128
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__token_sql') with nowait
        goto skip_fn__token_sql
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__token_sql') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__token_sql'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__token_sql]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:111128\s.zaglio: added key
    v:101230\s.zaglio: added option
    v:091128\s.zaglio: commonly used by fn__flds_quotename and sp__token
*/
CREATE function fn__token_sql(@name sysname)
returns bit
as
begin
if @name is null return null
if @name in (''delete'',''id'',''select'',''insert'',''update'',''name'',''group'',''backup'',''default'',
             ''set'',''table'',''view'',''as'',''where'',''from'',''to'',''go'',''by'',''having'',''create'',
             ''nocount'',''on'',''begin'',''end'',''if'',''else'',''exec'',''declare'',''return'',
             ''bit'',''nvarchar'',''varchar'',''int'',''bigint'',''char'',''nchar'',''sysname'',''datetime'',
             ''top'',''proc'',''function'',''goto'',''drop'',''not'',''is'',''null'',''option'',''key''
            )
    return 1
return 0
end -- fn__token_sql'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__token_sql:

-- ==================================================================== fn__trim
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__trim',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=070701
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__trim') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__trim') with nowait
        goto skip_fn__trim
        end
    if @ver>070701
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__trim') with nowait
        goto skip_fn__trim
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__trim') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__trim'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__trim]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:070701\S.Zaglio: remove left and right spaces
    t:print dbo.fn__trim(convert(nchar(20),''   hello   ''))
*/
create function fn__trim(@st nvarchar(4000))
returns nvarchar(4000)
as
begin
return ltrim(rtrim(@st))
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__trim:

-- ========================================================== fn__unix_timestamp
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__unix_timestamp',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120823
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__unix_timestamp') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__unix_timestamp') with nowait
        goto skip_fn__unix_timestamp
        end
    if @ver>120823
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__unix_timestamp') with nowait
        goto skip_fn__unix_timestamp
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__unix_timestamp') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__unix_timestamp'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__unix_timestamp]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    v:120823\s.zaglio: convert datetime to unix format
    t:select fn__unix_timestamp(getdate())
*/
CREATE function fn__unix_timestamp (
    @dt datetime
)
returns integer
as
begin
    declare @ret int
    select @ret = datediff(s,{d ''1970-01-01''}, @dt)
    return @ret
end -- fn__unix_timestamp'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__unix_timestamp:

-- ========================================================= fn__web_html_decode
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__web_html_decode',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130922
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__web_html_decode') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__web_html_decode') with nowait
        goto skip_fn__web_html_decode
        end
    if @ver>130922
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__web_html_decode') with nowait
        goto skip_fn__web_html_decode
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__web_html_decode') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__web_html_decode'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__web_html_decode]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130922\s.zaglio: @vcresult qas nv max
    v:121018\s.zaglio: decode url
    t:select dbo.fn__web_html_decode(
*/
CREATE function fn__web_html_decode (@vcwhat nvarchar(max))
returns nvarchar(max) as
begin
declare @vcresult nvarchar(max)
declare @vccrlf varchar(2)
declare @sipos smallint,@vcencoded varchar(7),@sichar smallint

select @vccrlf=crlf from fn__sym()

select @vcresult=@vcwhat
select @sipos=patindex(''%&#___;%'',@vcresult)
while @sipos>0
  begin
      select @vcencoded=substring(@vcresult,@sipos,6)
      select @sichar=cast(substring(@vcencoded,3,3) as smallint)
      select @vcresult=replace(@vcresult,@vcencoded,nchar(@sichar))
      select @sipos=patindex(''%&#___;%'',@vcresult)
  end

select @sipos=patindex(''%&#____;%'',@vcresult)
while @sipos>0
  begin
      select @vcencoded=substring(@vcresult,@sipos,7)
      select @sichar=cast(substring(@vcencoded,3,4) as smallint)
      select @vcresult=replace(@vcresult,@vcencoded,nchar(@sichar))
      select @sipos=patindex(''%&#____;%'',@vcresult)
  end

select @vcResult=replace(@vcResult,''&quot;'',''"'')
select @vcResult=replace(@vcResult,''&amp;'',''&'')
select @vcResult=replace(@vcResult,''&copy;'',''©'')
select @vcResult=replace(@vcResult,''&laquo;'',''«'')
select @vcResult=replace(@vcResult,''&raquo;'',''»'')
select @vcResult=replace(@vcResult,''&frac14;'',''¼'')
select @vcResult=replace(@vcResult,''&frac12;'',''½'')
select @vcResult=replace(@vcResult,''&iquest;'',''¿'')

select @vcResult=replace(@vcResult,''&lt;'',''<'')
select @vcResult=replace(@vcResult,''&gt;'',''>'')

select @vcresult=replace(@vcresult,''<p>'',@vccrlf)

return @vcresult
end -- fn__web_html_decode'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__web_html_decode:

-- ========================================================== fn__web_url_decode
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__web_url_decode',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__web_url_decode') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__web_url_decode') with nowait
        goto skip_fn__web_url_decode
        end
    if @ver>121018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__web_url_decode') with nowait
        goto skip_fn__web_url_decode
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__web_url_decode') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__web_url_decode'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__web_url_decode]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,web
    v:121018\s.zaglio: decode url
    t:select dbo.fn__web_url_decode(''jeff%2Bsmith'')
*/
CREATE function fn__web_url_decode(@url nvarchar(4000))
returns nvarchar(4000)
as
begin
-- from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=88926
declare @position int,
    @base char(16),
    @high tinyint,
    @low tinyint,
    @pattern char(21)

select    @base = ''0123456789abcdef'',
    @pattern = ''%[%][0-9a-f][0-9a-f]%'',
    @url = replace(@url, ''+'', '' ''),
    @position = patindex(@pattern, @url)

while @position > 0
    select    @high = charindex(substring(@url, @position + 1, 1), @base),
        @low = charindex(substring(@url, @position + 2, 1), @base),
        @url = stuff(@url, @position, 3, char(16 * @high + @low - 17)),
        @position = patindex(@pattern, @url)

    return    @url
end -- fn__web_url_decode'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__web_url_decode:

-- ========================================================== fn__web_url_encode
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__web_url_encode',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__web_url_encode') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__web_url_encode') with nowait
        goto skip_fn__web_url_encode
        end
    if @ver>121018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__web_url_encode') with nowait
        goto skip_fn__web_url_encode
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__web_url_encode') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__web_url_encode'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__web_url_encode]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,web
    v:121018\s.zaglio: encode url
    t:select dbo.fn__web_url_encode(''8ECEE9BE-05BD-4DD2-9D09-6C121E0924E7'')
*/
CREATE function dbo.fn__web_url_encode(@strInput nvarchar(4000))
returns nvarchar(4000)
as
begin
-- from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=88926
return
replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(
replace(replace(
        @strInput,
                ''%'', ''%25''),
            char(10), ''%0A''),
            char(13), ''%0D''),
                '' '', ''%20''),
                '':'', ''%3A''),
                '';'', ''%3B''),
                ''-'', ''%2D''),
                ''/'', ''%2F''),
                ''\'', ''%5C''),
                ''!'', ''%21''),
                ''"'', ''%22''),
                ''#'', ''%23''),
                ''?'', ''%3F''),
                ''='', ''%3D''),
                ''@'', ''%40''),
                ''>'', ''%3E''),
                ''<'', ''%3C''),
                ''$'', ''%24''),
                ''&'', ''%26''),
                ''['', ''%5B''),
                '']'', ''%5D''),
                ''~'', ''%7E''),
                ''^'', ''%5E''),
                ''`'', ''%60''),
                ''{'', ''%7B''),
                ''}'', ''%7D''),
                ''|'', ''%7C'')
end -- fn__web_url_encode'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__web_url_encode:

-- =============================================================== fn__word_find
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__word_find',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090815
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__word_find') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__word_find') with nowait
        goto skip_fn__word_find
        end
    if @ver>090815
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__word_find') with nowait
        goto skip_fn__word_find
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__word_find') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__word_find'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__word_find]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090815\s.zaglio: find a word in a sentence
    t:print dbo.fn__word_find(''convert(nvarchar,@test)'',''varchar'',default,default) -->9
    t:print dbo.fn__word_find(''declare @test nvarchar(10)'',''varchar'',default,default) -->15
    t:print dbo.fn__word_find(''varchar  10'',''varchar'',default,default) ->1
    t:print dbo.fn__word_find(''set nvarchar  10'',''varchar'',default,default) ->0
*/
CREATE function [dbo].[fn__word_find](
    @sentence nvarchar(4000),
    @word sysname,
    @lt sysname=null,
    @rt sysname=null
    )
returns int
as
begin
declare @i int,@c nchar
declare @key sysname
declare @points sysname
if @sentence is null return null
select @points='' ([,])''
if @lt is null select @lt=@points+char(9)
if @rt is null select @rt=@points+char(13)
select @key=''%''+replace(replace(@word,''_'',''[_]''),''%'',''[%]'')+''%''
/*  tests:
    print patindex(''%word%'',''wordcap'') -->1
    print patindex(''%_word%'',''wordcap'') -->0
    print patindex(''%_word%'',''inwordcap'') -->2
    print patindex(''%word%'',''demoword'') -->5
    if substring(''hello'',6,1)='''' print ''empty space''  -->empty space
    if substring(''hello'',-1,1)='''' print ''empty space''  -->empty space
*/
select @i=patindex(@key,@sentence)
if @i=0 return 0
select @c=substring(@sentence,@i+len(@word),1)
if @c!='''' and charindex(@c,@rt)=0 return 0
select @c=substring(@sentence,@i-1,1)
if @c!='''' and charindex(@c,@lt)=0 return 0
return @i
end -- function fn__word_find'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__word_find:

-- =========================================================== fn__words_replace
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__words_replace',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090815
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__words_replace') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__words_replace') with nowait
        goto skip_fn__words_replace
        end
    if @ver>090815
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__words_replace') with nowait
        goto skip_fn__words_replace
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__words_replace') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__words_replace'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__words_replace]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090815\s.zaglio: find a word in a sentence
    t:print dbo.fn__words_replace(
        '' declare @v nvarchar(12) nchar @v=convert(nvarchar,@varchar)'',
        default,
        ''varchar|char'',
        ''nvarchar|nchar'',
        default,
        default)
*/
CREATE function [dbo].[fn__words_replace](
    @sentence nvarchar(4000),
    @sep nvarchar(32)=''|'',
    @words sysname,
    @replaces sysname,
    @lt sysname=null,
    @rt sysname=null
    )
returns nvarchar(4000)
as
begin
declare
    @i int,@l int,@j int,@n int,
    @word sysname,@replace sysname
if @sentence is null return null
select @j=1,@n=dbo.fn__str_count(@words,@sep)
while @j<=@n
    begin
    select @word=dbo.fn__str_at(@words,@sep,@j)
    select @replace=dbo.fn__str_at(@replaces,@sep,@j)
    select @j=@j+1,@l=len(@word),@i=dbo.fn__word_find(@sentence,@word,@lt,@rt)
    while (@i>0)
        begin
        select @sentence=substring(@sentence,1,@i-1)+@replace+substring(@sentence,@i+@l,4000)
        select @i=dbo.fn__word_find(@sentence,@word,@lt,@rt)
        end -- while
    end -- while words
return @sentence
end -- function fn__word_find'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__words_replace:

-- ================================================================ fn__html_tag
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__html_tag',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120823.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__html_tag') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__html_tag') with nowait
        goto skip_fn__html_tag
        end
    if @ver>120823.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__html_tag') with nowait
        goto skip_fn__html_tag
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__html_tag') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__html_tag'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__html_tag]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:web
    v:120823.1000\s.zaglio: short comment
    c:from http://www.simple-talk.com
    c:/sql/t-sql-programming/getting-html-data-workbench/
    t:select * from fn__html_tag()
*/
CREATE function fn__html_tag()
returns table
as
return
SELECT [tag]=''!DOCTYPE'', [meaning]=''Defines the document type'',
     [type]=''Basic Tag'',[HasclosingTag]=0
UNION SELECT ''!|[CDATA|['', ''delimits a javascript area in XHTML'',''Basic Tag'',0
--note we have used the ESCAPE char | before the ''['' character
--as otherwise it would foul up the LIKE comparison
UNION SELECT ''?xml'', ''flags an XML document'',''Basic Tag'',0
UNION SELECT ''html'', ''Defines a html document'',''Basic Tag'',1
UNION SELECT ''body'', ''Defines the body element'',''Basic Tag'',1
UNION SELECT ''h1'', ''Defines header 1 '',''Basic Tag'',1
UNION SELECT ''h2'', ''Defines header 2 '',''Basic Tag'',1
UNION SELECT ''h3'', ''Defines header 3 '',''Basic Tag'',1
UNION SELECT ''h4'', ''Defines header 4 '',''Basic Tag'',1
UNION SELECT ''h5'', ''Definess header 5 '',''Basic Tag'',1
UNION SELECT ''h6'', ''Defines header 6 '',''Basic Tag'',1
UNION SELECT ''p'', ''Defines a paragraph'',''Basic Tag'',1
UNION SELECT ''br'', ''Inserts a single line break'',''Basic Tag'',0
UNION SELECT ''hr'', ''Defines a horizontal rule'',''Basic Tag'',0
UNION SELECT ''!--'', ''Defines a comment'',''Basic Tag'',0
UNION SELECT ''b'', ''Defines bold text'',''Char Format'',1
UNION SELECT ''font'', ''Defines the font face, size, and color of text'',
                                                   ''Char Format'',1
UNION SELECT ''i'', ''Defines italic text'',''Char Format'',1
UNION SELECT ''em'', ''Defines emphasized text '',''Char Format'',1
UNION SELECT ''big'', ''Defines big text'',''Char Format'',1
UNION SELECT ''strong'', ''Defines strong text'',''Char Format'',1
UNION SELECT ''small'', ''Defines small text'',''Char Format'',1
UNION SELECT ''sup'', ''Defines superscripted text'',''Char Format'',1
UNION SELECT ''sub'', ''Defines subscripted text'',''Char Format'',1
UNION SELECT ''bdo'', ''Defines the direction of text display'',
                                                   ''Char Format'',1
UNION SELECT ''u'', ''Defines underlined text'',''Char Format'',1
UNION SELECT ''pre'', ''Defines preformatted text'',''Output'',1
UNION SELECT ''code'', ''Defines computer code text'',''Output'',1
UNION SELECT ''tt'', ''Defines teletype text'',''Output'',1
UNION SELECT ''kbd'', ''Defines keyboard text'',''Output'',1
UNION SELECT ''dfn'', ''Defines a definition term'',''Output'',1
UNION SELECT ''var'', ''Defines a variable'',''Output'',1
UNION SELECT ''samp'', ''Defines sample computer code'',''Output'',1
UNION SELECT ''xmp'', ''Deprecated. Use <pre> instead'',''Output'',1
UNION SELECT ''acronym'', ''Defines an acronym'',''Blocks'',1
UNION SELECT ''abbr'', ''Defines an abbreviation'',''Blocks'',1
UNION SELECT ''address'', ''Defines an address element'',''Blocks'',1
UNION SELECT ''blockquote'', ''Defines an long quotation'',''Blocks'',1
UNION SELECT ''center'', ''Defines centered text'',''Blocks'',1
UNION SELECT ''q'', ''Defines a short quotation'',''Blocks'',1
UNION SELECT ''cite'', ''Defines a citation'',''Blocks'',1
UNION SELECT ''ins'', ''Defines inserted text'',''Blocks'',1
UNION SELECT ''del'', ''Defines deleted text'',''Blocks'',1
UNION SELECT ''s'', ''Defines strikethrough text'',''Blocks'',1
UNION SELECT ''strike'', ''Defines strikethrough text'',''Blocks'',1
UNION SELECT ''a'', ''Defines an anchor'',''Links'',1
UNION SELECT ''link'', ''Defines a resource reference'',''Links'',0
UNION SELECT ''frame'', ''Defines a sub window (a frame)'',''Frames'',1
UNION SELECT ''frameset'', ''Defines a set of frames'',''Frames'',1
UNION SELECT ''noframes'', ''Defines a noframe section'',''Frames'',1
UNION SELECT ''iframe'', ''Defines an inline sub window (frame)'',''Frames'',1
UNION SELECT ''form'', ''Defines a form '',''Input'',1
UNION SELECT ''input'', ''Defines an input field'',''Input'',0
UNION SELECT ''textarea'', ''Defines a text area'',''Input'',1
UNION SELECT ''button'', ''Defines a push button'',''Input'',1
UNION SELECT ''select'', ''Defines a selectable list'',''Input'',1
UNION SELECT ''optgroup'', ''Defines an option group'',''Input'',1
UNION SELECT ''option'', ''Defines an item in a list box'',''Input'',1
UNION SELECT ''label'', ''Defines a label for a form control'',''Input'',1
UNION SELECT ''fieldset'', ''Defines a fieldset'',''Input'',1
UNION SELECT ''legend'', ''Defines a title in a fieldset'',''Input'',1
UNION SELECT ''isindex'', ''Deprecated. Use <input> instead'',''Input'',1
UNION SELECT ''ul'', ''Defines an unordered list'',''Lists'',1
UNION SELECT ''ol'', ''Defines an ordered list'',''Lists'',1
UNION SELECT ''li'', ''Defines a list item'',''Lists'',1
UNION SELECT ''dir'', ''Defines a directory list'',''Lists'',1
UNION SELECT ''dl'', ''Defines a definition list'',''Lists'',1
UNION SELECT ''dt'', ''Defines a definition term'',''Lists'',1
UNION SELECT ''dd'', ''Defines a definition description'',''Lists'',1
UNION SELECT ''menu'', ''Defines a menu list'',''Lists'',1
UNION SELECT ''img'', ''Defines an image'',''Images'',0
UNION SELECT ''map'', ''Defines an image map '',''Images'',1
UNION SELECT ''area'', ''Defines an area inside an image map'',''Images'',0
UNION SELECT ''table'', ''Defines a table'',''Tables'',1
UNION SELECT ''caption'', ''Defines a table caption'',''Tables'',1
UNION SELECT ''th'', ''Defines a table header'',''Tables'',1
UNION SELECT ''tr'', ''Defines a table row'',''Tables'',1
UNION SELECT ''td'', ''Defines a table cell'',''Tables'',1
UNION SELECT ''thead'', ''Defines a table header'',''Tables'',1
UNION SELECT ''tbody'', ''Defines a table body'',''Tables'',1
UNION SELECT ''tfoot'', ''Defines a table footer'',''Tables'',1
UNION SELECT ''col'', ''Defines attributes for table columns '',''Tables'',0
UNION SELECT ''colgroup'', ''Defines groups of table columns'',''Tables'',1
UNION SELECT ''style'', ''Defines a style definition'',''Styles'',1
UNION SELECT ''div'', ''Defines a section in a document'',''Styles'',1
UNION SELECT ''span'', ''Defines a section in a document'',''Styles'',1
UNION SELECT ''head'', ''Defines information about the document'',''Meta Info'',1
UNION SELECT ''title'', ''Defines the document title'',''Meta Info'',1
UNION SELECT ''meta'', ''Defines meta information'',''Meta Info'',0
UNION SELECT ''base'', ''Defines base URL for all links in a page'',''Meta Info'',0
UNION SELECT ''basefont'', ''Defines a base font'',''Meta Info'',0
UNION SELECT ''script'', ''Defines a script'',''Programming'',1
UNION SELECT ''noscript'', ''Defines a noscript section'',''Programming'',1
UNION SELECT ''applet'', ''Defines an applet'',''Programming'',1
UNION SELECT ''object'', ''Defines an embedded object'',''Programming'',1
UNION SELECT ''param'', ''Defines a parameter for an object'',''Programming'',0
-- fn__web_tag'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__html_tag:

-- ===================================================================== fn__job
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__job',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131215
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__job') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__job') with nowait
        goto skip_fn__job
        end
    if @ver>131215
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__job') with nowait
        goto skip_fn__job
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__job') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__job'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__job]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    o:130209.1000\s.zaglio: fn__isjob
    v:131215\s.zaglio: maybe more compatible with future versions of mssql
    v:130209.1000\s.zaglio: return job info about @@spid
    t:sp__job_test
*/
CREATE function fn__job(@spid int)
returns table
as
return
select top 1 job_id,name,loginame
-- select top 1 *
from master..sysprocesses ss
join msdb..sysjobs
on  rtrim(ltrim(dbo.fn__str_between(ss.program_name,''job '','' :'',default)))
    =
    dbo.fn__hex(convert(varbinary,job_id))
where spid=@spid
and left([program_name],8)=''SQLAgent''
and hostname=left(@@servername,len(hostname))    -- istance is not passed
/*    this is not really necessary
and ltrim(rtrim(loginame)) in (
    dbo.fn__job_agent(),        /*    this return different values:
                                    LocalSystem instead of NT AUTH..\SYSTEM
                                    names in system language instead of local */
    ''NT AUTHORITY\SYSTEM'',''SQLServerAgentSVC'',    -- mssql2k and 2k5
    ''SERVIZIO DI RETE'',                            -- italian lang
    ''servicesql''                                -- on mssql2k12
    )
*/
-- fn__job'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__job:

-- ================================================================ fn__place_at
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__place_at',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121016
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__place_at') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__place_at') with nowait
        goto skip_fn__place_at
        end
    if @ver>121016
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__place_at') with nowait
        goto skip_fn__place_at
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__place_at') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__place_at'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__place_at]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:street, place, constant, type, at
    v:121016\s.zaglio: list "at" type of places
    t:select * from fn__place_at()
*/
CREATE function fn__place_at()
returns table
with schemabinding
as
return
select 1 lng,1 id,0 rid,''C/O'' cod       -- presso
union select 1,2,1,''PRESSO''
union select 1,3,1,''P/O''       -- presso
union select 1,4,1,''CASA DI RIPOSO''
union select 1,5,1,''REPARTO''
union select 1,6,1,''CENTRO DI RIABILITAZIONE''
union select 1,7,1,''RSA''
union select 1,8,1,''C\O''

-- end fn__place_at'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__place_at:

-- ============================================================== fn__place_type
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__place_type',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121016
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__place_type') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__place_type') with nowait
        goto skip_fn__place_type
        end
    if @ver>121016
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__place_type') with nowait
        goto skip_fn__place_type
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__place_type') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__place_type'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__place_type]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:street, place, constant, type
    v:121016\s.zaglio: list type of places
    t:select * from fn__place_type()
*/
CREATE function fn__place_type()
returns table
with schemabinding
as
return
select 1 lng,1 id,0 rid,''CASELLA POSTALE'' cod
union select 1,2,0,''CONTRADA''
union select 1,3,0,''CORSO''
union select 1,4,0,''FRAZIONE''
union select 1,5,0,''LIDO''
union select 1,6,0,''LOCALITÀ''
union select 1,7,0,''LUNGOMARE''
union select 1,8,0,''MOLO''
union select 1,9,0,''OSPEDALE''
union select 1,10,0,''PASSEGGIATA''
union select 1,11,0,''PIAZZA''
union select 1,12,0,''RACCORDO''
union select 1,13,0,''RIONE''
union select 1,14,0,''SCALO''
union select 1,15,0,''STRADA''
union select 1,16,0,''STRADINA''
union select 1,17,0,''STRADONE''
union select 1,18,0,''VIA''
union select 1,19,0,''VIALE''
union select 1,20,0,''VICOLO''
union select 1,21,0,''ZONA''
union select 1,22,2,''C.DA''
union select 1,23,3,''C.SO''
union select 1,24,11,''P.ZZA''
union select 1,25,11,''PZZA''
union select 1,26,19,''V.LE''
union select 1,27,0,''PIAZZALE''
union select 1,28,0,''VICO''
union select 1,29,4,''FRAZ.''
union select 1,31,0,''LARGO''
union select 1,32,0,''VILLA''
union select 1,33,31,''L.GO''
union select 1,34,18,''V.''
union select 1,35,6,''LOCALITA''''''
union select 1,36,15,''STD''
union select 1,37,0,''BORGO''
union select 1,38,37,''BGO''
union select 1,39,15,''S.DA''
union select 1,40,11,''P.ZA''
union select 1,41,0,''PIAZZETTA''
union select 1,42,41,''P.TTA''
union select 1,44,0,''TRAVERSA''
union select 1,45,44,''TRAV.''
union select 1,46,18,''VIA.''
-- union select 1,45,0,''C/O''       -- presso
-- union select 1,46,45,''PRESSO''
union select 1,47,3,''C/SO''       -- corso
union select 1,48,0,''LOC.''
union select 1,49,2,''C/DA''
union select 1,50,2,''CONTR.''
union select 1,51,3,''CSO''       -- corso
--union select 1,52,45,''P/O''       -- presso
union select 1,53,0,''DISTRETTO''
union select 1,54,0,''CORTE''
union select 1,55,19,''VLE''
union select 1,56,37,''B.GO''
union select 1,57,11,''P.''
union select 1,58,2,''CDA''
union select 1,59,15,''STR.COMUNALE''
union select 1,60,15,''STR.''
union select 1,61,3,''C.''
union select 1,62,0,''CENTRO DIREZIONALE''
union select 1,63,0,''PARCO''
union select 1,64,0,''GALLERIA''
union select 1,65,63,''PCO''       -- parco
union select 1,66,0,''POLICLINICO''
union select 1,67,0,''FARMACIA COMUNALE''
union select 1,68,0,''FARMACIA''
union select 1,69,67,''FARM.COMUN.''
union select 1,70,0,''CORTILE''
union select 1,71,32,''VILL.''
union select 1,72,48,''LOC''
union select 1,73,3,''C.SO''       -- corso
union select 1,74,0,''BORGATA''
union select 1,75,74,''B.TA''
-- union select 1,76,0,''CASA DI RIPOSO''    -- è un C/O, vedere fn__place_at
union select 1,77,0,''PASSAGGIO PRIVATO''
union select 1,78,15,''ST.''
union select 1,79,15,''STR''
union select 1,80,15,''STRDA''
union select 1,81,7,''L.MARE''
union select 1,82,7,''LG.MARE''
union select 1,83,2,''CONT.DA''
union select 1,84,2,''CON.DA''
union select 1,85,0,''CIRCONVALLAZIONE''
union select 1,86,0,''CENTRO COMMERCIALE''
union select 1,87,86,''CENTRO COMM.LE''
union select 1,88,45,''VICINO''
union select 1,89,0,''SALITA''
union select 1,90,0,''REGIONE''
union select 1,91,0,''CASCINA''
union select 1,94,0,''CENTRO VACANZE''  -- è un C/O, verificare presenza di name
union select 1,95,0,''RUE''   -- paesi di confine francese
-- end fn__place_type'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__place_type:

-- =================================================================== fn__range
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__range',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130808
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__range') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__range') with nowait
        goto skip_fn__range
        end
    if @ver>130808
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__range') with nowait
        goto skip_fn__range
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__range') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__range'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__range]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130808\s.zaglio: faster but for mssql2k5 or greater
    v:100501\s.zaglio: converted into @table
    v:090614\s.zaglio: return a numerator
    t:select row from fn__range(20,60,1)
    t:select * from fn__range(-20,-10,1)
    t:select row from fn__range(500,50000,1000)
    t:select * from fn__range(500,5000000,1000)
*/
CREATE function [dbo].[fn__range](@from int, @to int,@step int)
returns table
as
return
with a as (select 1 as n union all select 1) -- 2
     ,b as (select 1 as n from a ,a a1)       -- 4
     ,c as (select 1 as n from b ,b b1)       -- 16
     ,d as (select 1 as n from c ,c c1)       -- 256
     ,e as (select 1 as n from d ,d d1)       -- 65,536
     ,f as (select 1 as n from e ,e e1)       -- 4,294,967,296=17+trillion chrs
     ,factored as (select row_number() over (order by n) rn from f)
select (rn-1)*@step+@from as row
from factored
where rn<=(@to/@step-@from/@step+1)
and ((rn-1)*@step+@from)<=@to
-- fn__range'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__range:

-- =============================================================== fn__reg_parse
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__reg_parse',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__reg_parse') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__reg_parse') with nowait
        goto skip_fn__reg_parse
        end
    if @ver>100919
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__reg_parse') with nowait
        goto skip_fn__reg_parse
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__reg_parse') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__reg_parse'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__reg_parse]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100919\s.zaglio: parse registry name and split into parts
    t:select * from fn__reg_parse(''hklm\key1\key2\val'')
*/
create function fn__reg_parse(@key nvarchar(512))
returns table
as
return
select
    case left(@key,charindex(''\'',@key)-1)
    when ''hklm'' then ''hkey_local_machine''
    when ''hkcu'' then ''hkey_current_user''
    else left(@key,charindex(''\'',@key)-1)
    end as [root],

    substring(@key,charindex(''\'',@key)+1,
              dbo.fn__charindex(''\'',@key,-1)
              -charindex(''\'',@key)-1)
    as [key],

    substring(@key,dbo.fn__charindex(''\'',@key,-1)+1,128)
    as [value]
-- end fn__reg_parse'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__reg_parse:

-- ============================================================== fn__script_col
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__script_col',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120731
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__script_col') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__script_col') with nowait
        goto skip_fn__script_col
        end
    if @ver>120731
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__script_col') with nowait
        goto skip_fn__script_col
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_col') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_col'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_col]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:120731\s.zaglio: list cols info; used by sp__script...
    t:select * from fn__script_col(default) order by tablename,tableowner,columnid
*/
create function fn__script_col(@tbl_id int)
returns table
return
select top 100 percent
         sysobjects.name                                                   as tablename,
         sysusers.name                                                     as tableowner,
         c.name                                                            as columnname,
         c.colid                                                           as columnid,
         st.name                                                           as typename,
         case
           when bt.name in (N''nchar'',N''nvarchar'')
           then c.length / 2
           else c.length
         end as length,
         columnproperty(c.id,c.name,N''precisioN'')                          as [precision],
         columnproperty(c.id,c.name,N''scale'')                              as scale,
         convert(bit,columnproperty(c.id,c.name,N''isidentity''))            as [identity],
         bt.name                                                           as basetypename,
         convert(bit,c.iscomputed)                                         as iscomputed,
         convert(bit,columnproperty(c.id,c.name,N''isidnotforrepl''))        as notforreplication,
         convert(bit,columnproperty(c.id,c.name,N''allowsnull''))            as allownulls,
         case
           when (columnproperty(c.id,c.name,N''isidentity'') <> 0)
           then cast(ident_seed(''['' + sysusers.name + ''].['' + sysobjects.name + '']'') as decimal(38))
           else cast(0 as decimal(38))
         end as identityseed,
         case
           when (columnproperty(c.id,c.name,N''isidentity'') <> 0)
           then ident_incr(''['' + sysusers.name + ''].['' + sysobjects.name + '']'')
           else 0
         end as identityincrement,
         case
           when (objectproperty(c.cdefault,N''isdefaultcnst'') <> 0)
           then null
           else user_name(d.uid) + N''.'' + d.name
         end as defaultname,
         c.cdefault                                                        as defaulttextid,
         user_name(st.uid)                                                 as typeowner,
         user_name(r.uid) + N''.'' + r.name                                  as rulename,
         case
           when (objectproperty(c.cdefault,N''isdefaultcnst'') <> 0)
           then object_name(c.cdefault)
           else null
         end as dridefaultname,
         c.cdefault                                                        as defaultid,
         df.[text]                                                         as dridefaultcode,
         convert(bit,columnproperty(c.id,c.name,N''isfulltextindexed''))     as fulltextindexed,
         cc.text                                                           as computedtext,
         convert(bit,columnproperty(sysobjects.id,c.name,N''isrowguidcol'')) as isrowguidcol,
         c.collation                                                       as collation,
         c.language                                                        as fulltextlanguage,
         ftjoin.name                                                       as fulltexttypecolumn
from     dbo.syscolumns c with (nolock)
         inner join dbo.systypes st
           on st.xusertype = c.xusertype
         inner join dbo.systypes bt with (nolock)
           on bt.xusertype = c.xtype
         inner join dbo.sysobjects with (nolock)
           on sysobjects.id = c.id
         left join dbo.sysusers with (nolock)
           on sysusers.uid = sysobjects.uid
         left join dbo.sysobjects d with (nolock)
           on d.id = c.cdefault
         left join dbo.sysobjects r with (nolock)
           on r.id = c.domain
         left join dbo.syscomments cc with (nolock)
           on cc.id = sysobjects.id
              and cc.number = c.colid
         left join (select ftdep.id,
                           ftdep.number,
                           ftcol2.name
                    from   sysdepends ftdep
                           left join syscolumns ftcol2
                             on ftcol2.colid = ftdep.depnumber
                    where  columnproperty(ftdep.id,ftcol2.name,''istypeforfulltextblob'') = 1
                           and ftdep.id = ftdep.depid
                           and ftdep.id = ftcol2.id) ftjoin
           on ftjoin.id = c.id
              and ftjoin.number = c.colid
              and ftjoin.id = c.id
         left join dbo.syscomments df with (nolock)
           on df.id=c.cdefault
where    objectproperty(c.id,''istable'') = 1
         and objectproperty(c.id,''issystemtable'') = 0
         and (@tbl_id is null or c.id=@tbl_id)
-- end fn__script_col'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_col:

-- ============================================================== fn__script_idx
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__script_idx',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121005
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__script_idx') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__script_idx') with nowait
        goto skip_fn__script_idx
        end
    if @ver>121005
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__script_idx') with nowait
        goto skip_fn__script_idx
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_idx') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_idx'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_idx]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:121005\s.zaglio: done previous
    v:121004\s.zaglio: a bug near included columns
    v:120802\s.zaglio: added more column info and index_id
    v:120731\s.zaglio: adapted to mssql 2k5
    v:120305\s.zaglio: list indexes info (used by sp__script..)
    t:select * from fn__script_idx(object_id(''cfg''))
*/
CREATE function fn__script_idx(@tbl_id int=null)
returns table
as
return
select
       sysindexes.id                                                              as [object_id],
       sysindexes.indid                                                           as index_id,
       six.index_column_id                                                        as index_column_id,
       sysindexkeys.keyno                                                         as keyno,
       convert(bit,indexproperty(sysindexes.id,sysindexes.name,N''isclustered''))   as [clustered],
       convert(bit,indexproperty(sysindexes.id,sysindexes.name,N''isunique''))      as [unique],
       convert(bit,case
                     when (sysindexes.status & 4096) = 0
                     then 0
                     else 1
                   end)                                                           as uniqueconstraint,
       convert(bit,case
                     when (sysindexes.status & 2048) = 0
                     then 0
                     else 1
                   end)                                                           as [primary],
       convert(bit,case
                     when (sysindexes.status & 0x1000000) = 0
                     then 0
                     else 1
                   end)                                                           as norecompute,
       convert(bit,case
                     when (sysindexes.status & 0x1) = 0
                     then 0
                     else 1
                   end)                                                           as ignoredupkey,
       convert(bit,indexproperty(sysindexes.id,sysindexes.name,N''ispadindex''))    as ispadindex,
       convert(bit,objectproperty(sysindexes.id,N''istable''))                      as istable,
       convert(bit,objectproperty(sysindexes.id,N''isview''))                       as isview,
       convert(bit,indexproperty(sysindexes.id,sysindexes.name,N''isfulltextkey'')) as fulltextkey,
       convert(bit,indexproperty(sysindexes.id,sysindexes.name,N''isstatistics''))  as [statistics],
       sysfilegroups.groupname                                                    as filegroup,
       sysobjects.name                                                            as parentname,
       sysusers.name                                                              as parentowner,
       sysindexes.name                                                            as indexname,
       sysindexes.origfillfactor                                                  as [fillfactor],
       sysindexes.status,
       syscolumns.name                                                            as columnname,
       syscolumns.xusertype                                                       as columnxusertype,
       systypes.name                                                              as columntype,
       convert(bit,isnull(indexkey_property(syscolumns.id,sysindexkeys.indid,keyno,''isdescending''),
                          0))                                                     as descending,
       isnull(six.is_included_column,0)                                           as is_included_column

from    sysindexes with (nolock)                       -- select * from sysindexes where name=''ix_log_ddl''
        inner join sysindexkeys with (nolock)          -- select * from sysindexkeys where indid=2 and id=2103730597
           on sysindexes.indid = sysindexkeys.indid     -- select * from sys.indexes where name=''ix_log_ddl''
              and sysindexkeys.id = sysindexes.id       -- select * from sys.indexkeys where indid=2 and id=2103730597
        inner join syscolumns with (nolock)            -- select * from syscolumns where id=2103730597 and colid in (1,6,12,4)
           on syscolumns.colid = sysindexkeys.colid     -- select * from sys.index_columns where object_id=2103730597 and index_id=2
              and syscolumns.id = sysindexes.id
        inner join sysobjects with (nolock)
           on sysobjects.id = sysindexes.id
        left join sysusers with (nolock)
           on sysusers.uid = sysobjects.uid
        left join sysfilegroups with (nolock)
           on sysfilegroups.groupid = sysindexes.groupid
        join sys.index_columns six
            on sysindexes.id  = six.[object_id]
            and sysindexes.indid  = six.[index_id]
            and sysindexkeys.keyno = six.key_ordinal
            and sysindexkeys.colid = six.column_id
            -- and six.is_included_column=1
        join systypes
            on syscolumns.xusertype=systypes.xusertype
where    (objectproperty(sysindexes.id,''istable'') = 1
           or objectproperty(sysindexes.id,''isview'') = 1)
         and objectproperty(sysindexes.id,''issystemtable'') = 0
         and indexproperty(sysindexes.id,sysindexes.name,N''isautostatistics'') = 0
         and indexproperty(sysindexes.id,sysindexes.name,N''ishypothetical'') = 0
         and sysindexes.name is not null
         and (@tbl_id is null or sysobjects.id=@tbl_id)
-- fn__script_idx'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_idx:

-- ============================================================== fn__script_tbl
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__script_tbl',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120731
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__script_tbl') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__script_tbl') with nowait
        goto skip_fn__script_tbl
        end
    if @ver>120731
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__script_tbl') with nowait
        goto skip_fn__script_tbl
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_tbl') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_tbl'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_tbl]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:120731\s.zaglio: list tbls info; used by sp__script...
    t:select * from fn__script_tbl(default)
*/
CREATE function fn__script_tbl(@tbl_id int)
returns table
return
select
       o.name,
       user_name(o.uid)                                                as owner,
       o.id,
       convert(bit,objectproperty(o.id,''tablehasactivefulltextindex'')) as fulltextindexed,
       sysfulltextcatalogs.name                                        as fulltextcatalogname,
       sysfilegroups.groupname                                         as filegroup,
       (select top 1 groupname
        from   sysfilegroups
               inner join sysindexes
                 on sysindexes.groupid = sysfilegroups.groupid
        where  sysindexes.indid = 255
               and sysindexes.id = o.id)                               as textfilegroup
from     dbo.sysobjects o with (nolock)
         left join sysfulltextcatalogs with (nolock)
           on sysfulltextcatalogs.ftcatid = o.ftcatid
         left join sysindexes with (nolock)
           on sysindexes.id = o.id
         left join sysfilegroups with (nolock)
           on sysfilegroups.groupid = sysindexes.groupid
where    objectproperty(o.id,N''istable'') = 1
         and objectproperty(o.id,N''ismsshipped'') = 0
         and objectproperty(o.id,N''issystemtable'') = 0
         and objectproperty(o.id,N''tableisfake'') = 0
         and sysindexes.indid < 2
         and (@tbl_id is null or o.id=@tbl_id)
-- end fn__script_tbl'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_tbl:

-- ============================================================== fn__script_trg
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__script_trg',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120731
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__script_trg') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__script_trg') with nowait
        goto skip_fn__script_trg
        end
    if @ver>120731
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__script_trg') with nowait
        goto skip_fn__script_trg
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_trg') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_trg'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_trg]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:120731\s.zaglio: list cols info; used by sp__script...
    t:select * from fn__script_trg(default) order by tablename,tableowner,columnid
*/
create function fn__script_trg(@tbl_id int)
returns table
return
select
       sysobjects.id,
       sysobjects.name                                                       as triggername,
       u2.name                                                               as triggerowner,
       t.name                                                                as parentname,
       t.id                                                                  as parentid,
       sysusers.name                                                         as parentowner,
       convert(bit,objectproperty(sysobjects.id,''execisupdatetrigger''))      as isupdatetrigger,
       convert(bit,objectproperty(sysobjects.id,''execisdeletetrigger''))      as isdeletetrigger,
       convert(bit,objectproperty(sysobjects.id,''execisinserttrigger''))      as isinserttrigger,
       convert(bit,objectproperty(sysobjects.id,''execisaftertrigger''))       as isaftertrigger,
       convert(bit,objectproperty(sysobjects.id,''execisinsteadoftrigger''))   as isinsteadoftrigger,
       convert(bit,objectproperty(sysobjects.id,''execisquotedidentoN''))      as quotedidentifier,
       convert(bit,objectproperty(sysobjects.id,''execisansinullsoN''))        as ansinulls,
       convert(bit,objectproperty(sysobjects.id,''execisfirstdeletetrigger'')) as execisfirstdeletetrigger,
       convert(bit,objectproperty(sysobjects.id,''execisfirstinserttrigger'')) as execisfirstinserttrigger,
       convert(bit,objectproperty(sysobjects.id,''execisfirstupdatetrigger'')) as execisfirstupdatetrigger,
       convert(bit,objectproperty(sysobjects.id,''execislastdeletetrigger''))  as execislastdeletetrigger,
       convert(bit,objectproperty(sysobjects.id,''execislastinserttrigger''))  as execislastinserttrigger,
       convert(bit,objectproperty(sysobjects.id,''execislastupdatetrigger''))  as execislastupdatetrigger,
       convert(bit,objectproperty(sysobjects.id,''execistriggerdisabled''))    as isdisabled,
       convert(bit,objectproperty(t.id,''istable''))                           as istable,
       convert(bit,objectproperty(t.id,''isview''))                            as isview,
       syscomments.encrypted                                                 as encrypted
from     sysobjects with (nolock)
         inner join sysobjects t with (nolock)
           on t.id = sysobjects.parent_obj
         left join sysusers with (nolock)
           on sysusers.uid = t.uid
         left join sysusers u2 with (nolock)
           on u2.uid = sysobjects.uid
         left join syscomments with (nolock)
           on sysobjects.id = syscomments.id
where    sysobjects.type = ''tr''
         and isnull(syscomments.colid,1) = 1
         and (@tbl_id is null or sysobjects.parent_obj=@tbl_id)
-- end fn__script_trg'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_trg:

-- ============================================================= fn__server_info
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__server_info',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130517
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__server_info') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__server_info') with nowait
        goto skip_fn__server_info
        end
    if @ver>130517
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__server_info') with nowait
        goto skip_fn__server_info
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__server_info') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__server_info'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__server_info]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    d:130517\s.zaglio:sp__clientip
    d:130517\s.zaglio:sp__serverip
    d:130517\s.zaglio:sp__parse_conn
    v:130517\s.zaglio:return info about this server, changed from txt to tbl
    v:081003\s.zaglio:old version
    t:select * from fn__server_info()
*/
CREATE function fn__server_info()
returns table
as
return
select
    coalesce(convert(sysname,serverproperty(''machinename'')),'''') as machine_name,
    coalesce(convert(sysname,serverproperty(''servername'') ),'''') as server_name,
    local_net_address as server_ip,
    coalesce(convert(sysname,serverproperty(''instancename'')),'''') as instance_name,
    local_tcp_port as instance_port,
    coalesce(convert(sysname,serverproperty(''edition'') ),'''') as edition,
    coalesce(convert(sysname,serverproperty(''productversion'') ),'''') as product_version,
    coalesce(convert(sysname,serverproperty(''productlevel'') ),'''') as product_level,
    coalesce(convert(sysname,serverproperty(''engineedition'')),'''') as engine_edition,
    coalesce(convert(sysname,serverproperty(''computernamephysicalnetbios'')),'''') as computer_name_physical_netbios

-- select *
from sys.dm_exec_connections
where session_id=@@spid
-- fn__server_info'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__server_info:

-- ======================================================= fn__sessionproperties
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sessionproperties',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110126
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sessionproperties') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sessionproperties') with nowait
        goto skip_fn__sessionproperties
        end
    if @ver>110126
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sessionproperties') with nowait
        goto skip_fn__sessionproperties
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sessionproperties') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sessionproperties'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sessionproperties]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110126\s.zaglio: return all session properties
    t:select * from fn__sessionproperties()
*/
create function fn__sessionproperties()
returns table
as
return
select SESSIONPROPERTY(''ANSI_NULLS'') VALUE,''ANSI_NULLS'' as SESSIONPROPERTY
union
select SESSIONPROPERTY(''ANSI_PADDING'') VALUE,''ANSI_PADDING'' as SESSIONPROPERTY
union
select SESSIONPROPERTY(''ANSI_WARNINGS'') VALUE,''ANSI_WARNINGS'' as SESSIONPROPERTY
union
select SESSIONPROPERTY(''ARITHABORT'') VALUE,''ARITHABORT'' as SESSIONPROPERTY
union
select SESSIONPROPERTY(''CONCAT_NULL_YIELDS_NULL'') VALUE,''CONCAT_NULL_YIELDS_NULL'' as SESSIONPROPERTY
union
select SESSIONPROPERTY(''NUMERIC_ROUNDABORT'') VALUE,''NUMERIC_ROUNDABORT'' as SESSIONPROPERTY
union
select SESSIONPROPERTY(''QUOTED_IDENTIFIER'') VALUE,''QUOTED_IDENTIFIER'' as SESSIONPROPERTY'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sessionproperties:

-- ============================================================== fn__str_params
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_params',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130909
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_params') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_params') with nowait
        goto skip_fn__str_params
        end
    if @ver>130909
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_params') with nowait
        goto skip_fn__str_params
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_params') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_params'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_params]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130909\s.zaglio: optimized using with
    v:111114\s.zaglio: optimized and added to grp util_tkns
    v:100718\s.zaglio: same as fn__str_table but reduce tokens
    t:
        select * from dbo.fn__str_params(''a,b  ,c,
                                         d,  e  ,  f'','','',default)
*/
CREATE function [dbo].[fn__str_params](
    @data nvarchar (4000),
    @sep nvarchar (32)=''|'',
    @reserved bit=null
    )
returns table
as
return
-- declare @data sysname,@sep sysname
-- select @data='' a b c '',@sep='' '';
with pieces(pos, start, [stop]) as (
  select 1, 1, charindex(@sep, @data)
  union all
  select
    pos + 1,
    [stop] + (datalength(@sep)/2),
    charindex(@sep, @data, [stop] + (datalength(@sep)/2))
  from pieces
  where [stop] > 0
)
select pos,
    token=ltrim(rtrim(replace(replace(replace(
            substring(@data, start,
                      case when [stop] > 0 then [stop]-start else 4000 end
                     ),
            cr,''''),lf,''''),tab,'''')))
from pieces,fn__sym()
-- fn__str_table_fast'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_params:

-- =============================================================== fn__str_split
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_split',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130909
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_split') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_split') with nowait
        goto skip_fn__str_split
        end
    if @ver>130909
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_split') with nowait
        goto skip_fn__str_split
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_split') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_split'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_split]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    o:130909\s.zaglio: fn__str_table_fast
    c:http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings
    c:option (maxrecursion 0)
    v:130909\s.zaglio: unified fn__str_split and fn__str_table_fast
    v:120909\s.zaglio: faster version of fn__str_table for few lines
    t:select * from dbo.fn__str_split(''a|b|c'',''|'')
    t:select * from dbo.fn__str_split(''a\n\nc'',''\n'') <-- is correct have only one line empty
    t:select * from dbo.fn__str_split(null,'';'')
    t:select * from dbo.fn__str_split(''a;b;c'',null) -- a;b;c
    t:select * from dbo.fn__str_split(''one two tree four  test'','''')
    t:select * from dbo.fn__str_split(''one, two, tree,'','','')
    t:
        select l,s.* from (
            select ''1|a|aa'' as l union
            select ''2|b|bb'' as l union
            select ''3|c|cc'' as l union
            select ''4|d|dd'' as l
        ) as data
        cross apply dbo.fn__str_table_fast(data.l,''|'') s
    t:select * from fn__Str_table(''a b c'','''')
    t:select * from fn__Str_table_fast(''a b c'','''')
*/
CREATE function [dbo].[fn__str_split](
  @data nvarchar (4000),
  @sep nvarchar (32)=''|''
  )
returns table
as
return
-- declare @data sysname,@sep sysname
-- select @data='' a b c '',@sep='' '';
with pieces(pos, start, [stop]) as (
  select
    1, 1,
    charindex(case @sep when '''' then '' '' else @sep end, @data)
  union all
  select
    pos + 1, [stop] + (datalength(sep)/2),
    charindex(sep, @data, [stop] + (datalength(sep)/2))
  from pieces,
       (select case @sep when '''' then '' '' else @sep end sep) sep
  where [stop] > 0
)
select pos,
  substring(@data, start,
            case when [stop] > 0 then [stop]-start else 4000 end
            ) as token
  -- ,len(sep) ns
from pieces
-- fn__str_table_fast'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_split:

-- ========================================================== fn__str_table_fast
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_table_fast',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130909
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_table_fast') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_table_fast') with nowait
        goto skip_fn__str_table_fast
        end
    if @ver>130909
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_table_fast') with nowait
        goto skip_fn__str_table_fast
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_table_fast') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_table_fast'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_table_fast]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    a:130909\s.zaglio: fn__str_split
    v:130909\s.zaglio: unified fn__str_split and fn__str_table_fast
    v:120909\s.zaglio: faster version of fn__str_table for few lines
    t:select * from dbo.fn__str_split(''a|b|c'',''|'')
    t:select * from dbo.fn__str_split(''a\n\nc'',''\n'') <-- is correct have only one line empty
    t:select * from dbo.fn__str_split(null,'';'')
    t:select * from dbo.fn__str_split(''a;b;c'',null) -- a;b;c
    t:select * from dbo.fn__str_split(''one two tree four  test'','''')
    t:select * from dbo.fn__str_split(''one, two, tree,'','','')
    t:
        select l,s.* from (
            select ''1|a|aa'' as l union
            select ''2|b|bb'' as l union
            select ''3|c|cc'' as l union
            select ''4|d|dd'' as l
        ) as data
        cross apply dbo.fn__str_table_fast(data.l,''|'') s
    t:select * from fn__Str_table(''a b c'','''')
    t:select * from fn__Str_table_fast(''a b c'','''')
*/
CREATE function [dbo].[fn__str_table_fast](
  @data nvarchar (4000),
  @sep nvarchar (32)=''|''
  )
returns table
as
return
-- declare @data sysname,@sep sysname
-- select @data='' a b c '',@sep='' '';
with pieces(pos, start, [stop]) as (
  select
    1, 1,
    charindex(case @sep when '''' then '' '' else @sep end, @data)
  union all
  select
    pos + 1, [stop] + (datalength(sep)/2),
    charindex(sep, @data, [stop] + (datalength(sep)/2))
  from pieces,
       (select case @sep when '''' then '' '' else @sep end sep) sep
  where [stop] > 0
)
select pos,
  substring(@data, start,
            case when [stop] > 0 then [stop]-start else 4000 end
            ) as token
  -- ,len(sep) ns
from pieces
-- fn__str_table_fast'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_table_fast:

-- =========================================================== fn__comment_types
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__comment_types',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100612
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__comment_types') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__comment_types') with nowait
        goto skip_fn__comment_types
        end
    if @ver>100612
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__comment_types') with nowait
        goto skip_fn__comment_types
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__comment_types') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__comment_types'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__comment_types]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100612\s.zaglio: managed unknown objs (retur nothing)
    v:100328\s.zaglio: support for fn__comment,sp__comment
    todo: using MS original methos is slow. Need to be rewritten
    t:
        select * from dbo.fn__comment_types(''sp__comment'')
        select * from dbo.fn__comment_types(''sp__comment.@path'')
        select * from dbo.fn__comment_types(''fn__comment.@path'')
*/
CREATE function [dbo].[fn__comment_types] (
    @path sysname=null
    )
returns @t table (
    prop sysname,
    sch sysname,sch_type sysname,
    obj sysname,obj_type sysname,
    sub sysname null,sub_type sysname null,
    xtype nvarchar(2),id int
    )
as
begin

declare
    @sch sysname,@sch_type sysname,
    @obj sysname,@obj_type sysname,
    @sub sysname,@sub_type sysname,
    @id int,@prop sysname,@xtype nvarchar(2)

if @path is null return

/*
Table 1. Extended property hierarchy for SQL Server 2000.
------------------------------------------------------------------------
Level 0                 Level 1                 Level 2
User                    Table                   Column, index, constraint, trigger
User                    View                    Column, INSTEAD OF trigger
User                    Schema-bound view       Column, index, INSTEAD OF trigger
User                    Procedure               Parameter
User                    Rule                    (none)
User                    Default                 (none)
User                    Function                Column, parameter, constraint
User                    Schema-bound function   Column, parameter, constraint
User-defined datatype   (none)                  (none)
*/

select @prop=N''MS_Description'',
       @sch_type=''Schema''  -- maybe in mssql2k must use ''user''

if charindex(''.'',@path)=0
    select
        @obj=parsename(@path,1)
else
    select
        @sch=parsename(@path,3),
        @obj=parsename(@path,2),
        @sub=parsename(@path,1)

if @sch is null select @sch=[name] from dbo.fn__schema_of(@obj)

select @id=object_id(@sch+''.''+@obj)
if @id is null return

select @xtype=xtype from sysobjects where id=@id

if @xtype in (''V'') select  @obj_type=''View'',     @sub_type=''Column''
if @xtype in (''U'') select  @obj_type=''Table'',    @sub_type=''Column''
if @xtype in (''P'',''TR'') select  @obj_type=''Procedure'',@sub_type=''Parameter''
if @xtype in (''FN'',''IF'',''TF'',''IT'') select  @obj_type=''Function'',@sub_type=''Parameter''
-- select distinct xtype from sysobjects

if charindex(''.'',@path)=0 select @sub_type=null

insert @t select @prop,
                 @sch,@sch_type,@obj,@obj_type,
                 @sub,@sub_type,@xtype,@id
return
end -- fn__comment_types'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__comment_types:

-- ================================================================ fn__comments
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__comments',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__comments') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__comments') with nowait
        goto skip_fn__comments
        end
    if @ver>100919
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__comments') with nowait
        goto skip_fn__comments
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__comments') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__comments'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__comments]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100919\s.zaglio: compilable in mssql2k but not executable
    v:100228\s.zaglio: added obj_id and null on @table
    v:100204\s.zaglio: return comment of obj
    t:
        create table test_info
            (
            a nchar(10) null,
            b nchar(10) null
            )  on [primary]
        exec sp__comment ''test_info'',''table comment''
        execute sp_addextendedproperty
            N''ms_description'', N''description col a'',
            N''schema'', N''dbo'', N''table'', N''test_info'', N''column'', N''a''

        select * from dbo.fn__comments(''test_info'')

        drop table test_info
*/
CREATE function [dbo].[fn__comments](@table sysname)
returns @t table(obj_id int,column_name sysname null,value nvarchar(4000) null)
as
begin
declare @id int select @id=object_id(@table)
-- select top 10 * from syscolumns
if dbo.fn__isMSSQL2K()=1
    insert @t(obj_id,column_name,value)
    select id,col,com from (
        select      o.id id,
                    o.name as col,
                    convert(nvarchar(4000),p.value) as com,
                    0 as ord
        from    sysobjects o
        join    sysproperties p
                    on o.id = p.id
                    and 0 = p.smallid
        where       p.name=''MS_Description''
        and         @table is null or o.id=@id

        union

        select      o.id id,
                    c.name as col,
                    convert(nvarchar(4000),p.value) as com,
                    c.colorder as ord
        from    sysobjects o
        join    sysproperties p
                    on o.id = p.id
        join    syscolumns c
                    on p.smallid = c.colid
                    and p.id = c.id
        where       p.name=''MS_Description''
        and         @table is null or o.id=@id
    ) qry
    order by ord
else
    insert @t
    select id,col,com from (
        select      o.object_id id, null as col,
                    convert(nvarchar(4000),ep.value) as com,
                    0 as ord
        from        sys.objects o
        join        sys.extended_properties ep
                    on o.object_id = ep.major_id and 0=ep.minor_id
        join        sys.schemas s
                    on o.schema_id = s.schema_id
        where       ep.name=''MS_Description''
        and         @table is null or o.object_id=@id

        union

        select      o.object_id id, c.name as col,
                    convert(nvarchar(4000),ep.value) as com,
                    c.colorder as ord
        from        sys.objects o
        join        sys.extended_properties ep
                    on o.object_id = ep.major_id
        join        sys.schemas s
                    on o.schema_id = s.schema_id
        join        syscolumns c
                    on ep.minor_id = c.colid
                    and ep.major_id = c.id
        where       ep.name=''MS_Description''
        and         @table is null or o.object_id=@id
    ) qry
    order by ord
return
/*
SQLSERVER 2000:

SELECT    sysobjects.Name AS ObjectName,
            sysobjects.xtype AS ObjectType,
            user_name(sysobjects.uid) AS SchemaOwner,
            sysproperties.name AS PropertyName,
            sysproperties.value AS PropertyValue,
            syscolumns.name AS ColumnName,
            syscolumns.colid AS Ordinal
FROM    sysobjects INNER JOIN sysproperties
            ON sysobjects.id = sysproperties.id
            LEFT JOIN syscolumns
            ON sysproperties.smallid = syscolumns.colid
            AND sysproperties.id = syscolumns.id
ORDER BY SchemaOwner, ObjectName, ObjectType, Ordinal

SQLSERVER 2005:
SELECT        o.Name AS ObjectName,
            o.type AS ObjectType,
            s.name AS SchemaOwner,
            ep.name AS PropertyName,
            ep.value AS PropertyValue,
            c.name AS ColumnName,
            c.colid AS Ordinal
FROM        sys.objects o INNER JOIN sys.extended_properties ep
            ON o.object_id = ep.major_id
            INNER JOIN sys.schemas s
            ON o.schema_id = s.schema_id
            LEFT JOIN syscolumns c
            ON ep.minor_id = c.colid
            AND ep.major_id = c.id
WHERE        o.type IN (''V'', ''U'', ''P'')
ORDER BY    SchemaOwner,ObjectName, ObjectType, Ordinal
*/
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__comments:

-- ===================================================================== fn__dir
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__dir',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131015
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__dir') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__dir') with nowait
        goto skip_fn__dir
        end
    if @ver>131015
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__dir') with nowait
        goto skip_fn__dir
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__dir') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__dir'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__dir]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:sub,folder,directory,list,sub,folder,s,sp__dir,unicode
    c:from https://www.simple-talk.com/iwritefor/articlefiles/634-dboDIR.htm
    v:131015\s.zaglio: done, but abbandoned because 6-20 times slower than sp
    r:131014\s.zaglio: list content of path and sub paths
    t:select * from fn__dir(''I:\i_do_not_exists'',default) -- raiserror
    t:select * from fn__dir(''I:\temp\'',default)
    t:select * from fn__dir(''I:\temp'',''s'')
    t:select * from fn__dir(''I:\temp'',''s|kp'')
*/
CREATE function [dbo].[fn__dir](@wildcard nvarchar(4000),@opt sysname)
returns @dir table
(
    id int identity primary key,
    rid int,                -- for subdirs
    [flags] smallint,       -- if &32=32 is a <DIR>;16 is a error
    [key] nvarchar(446),    -- obj name or error string
    dt datetime,            -- creation date
    n bigint null           -- size in bytes
)
as
-- body of the function
begin
declare
    --all the objects used
    @objshellapplication int,
    @objfolder int,
    @objitem int,
    @objerrorobject int,
    @objfolderitems int,
    --potential error message shows where error occurred.
    @strerrormessage nvarchar(1000),
    --command sent to ole automation
    @command nvarchar(1000),
    @hr int,                            --ole result (0 if ok)
    @count int,@i int,
    @name nvarchar(2000),               --the name of the current item
    @path nvarchar(2000),
    @type nvarchar(2000),
    @modifydate datetime,               --the date the current item last modified
    @isfilesystem int,                  --1 if the current item is part of the file system
    @isfolder int,                      --1 if the current item is a file
    @size bigint,
    @rid int,
    @lp int,                            --len path

    -- options
    @s bit,                             --scan subdirectory
    @kp bit                             --keep root path

if len(coalesce(@wildcard,''''))<2 return

if not @opt is null
    begin
    select @name=''|''+@opt+''|''           --to not |||grow||| in recursion
    select @s=charindex(''|s|'',@name),@kp=charindex(''|kp|'',@name)
    end
else
    select @kp=0,@s=0

if right(@wildcard,1)=''\'' select @wildcard=left(@wildcard,len(@wildcard)-1)

if @kp=0 select @path=@wildcard+''\'',@lp=len(@path)

select @strerrormessage = ''opening the shell application object''
select @objshellapplication=0
exec @hr = sp_oacreate ''shell.application'', @objshellapplication out
if @hr!=0 goto dispose

--now we get the folder.
select  @objerrorobject = @objshellapplication,
       @strerrormessage = ''getting folder"'' + @wildcard + ''"'',
       @command = ''namespace("''+@wildcard+''")''

select @objfolder=0
exec @hr = sp_oamethod @objshellapplication, @command, @objfolder out
if @objfolder is null
    begin
    exec sp_oadestroy @objshellapplication
    return --nothing there. sod the error message
    end

--and then the number of objects in the folder
select  @objerrorobject = @objfolder,
       @strerrormessage = ''getting count of folder items in "''+@wildcard+''"'',
       @command = ''items.count''
exec @hr = sp_oamethod @objfolder, @command, @count out
--now get the folderitems collection
select  @objerrorobject = @objfolder,
        @strerrormessage = '' getting folderitems'',
       @command=''items()''
exec @hr = sp_oamethod @objfolder, @command, @objfolderitems output

select @i = 0, @rid = 0

--iterate through the folderitems collection
--http://msdn.microsoft.com/en-us/library/windows/desktop/bb787810(v=vs.85).aspx
while @hr=0 and @i<@count
    begin
    select @objerrorobject = @objfolderitems,
           @strerrormessage = '' getting folder item ''+cast(@i as varchar(5)),
           @command=''item('' + cast(@i as varchar(5))+'')''
           --@command=''getdetailsof(''+ cast(@i as varchar(5))+'',1)''
    exec @hr = sp_oamethod @objfolderitems, @command, @objitem output
    select  @objerrorobject = @objitem,
            @strerrormessage = '' getting folder item properties''
                   + cast(@i as varchar(5))

    exec @hr = sp_oamethod @objitem,''path'', @name output
    if @hr!=0 break
    if @kp=0 select @name=stuff(@name,1,@lp,'''')

    exec @hr = sp_oamethod @objitem,''type'', @type output
    if @hr!=0 break

    exec @hr = sp_oamethod @objitem,''modifydate'', @modifydate output
    if @hr!=0 break

    exec @hr = sp_oamethod @objitem,''size'', @size output
    if @hr!=0 break
    /*
    exec @hr = sp_oamethod @objitem,''isfilesystem'', @isfilesystem output
    if @hr!=0 break
    */
    exec @hr = sp_oamethod @objitem,''isfolder'', @isfolder output
    if @hr!=0 break

    --and insert the properties into a table
    insert into @dir (rid, flags, [key], dt, n)
    select
        @rid,
        case @isFolder when 1 then 32 else 0 end,
        @name,
        @modifydate,
        @size

    exec sp_oadestroy @objitem
    select @objitem=0

    select @i=@i+1
    end -- files loop

dispose:

if @hr <> 0
    begin
    declare
        @source nvarchar(255),
        @description nvarchar(255),
        @helpfile nvarchar(255),
        @helpid int

    exec sp_oageterrorinfo @objerrorobject, @source output,
        @description output, @helpfile output, @helpid output
    select  @strerrormessage = ''error whilst ''
            + coalesce(@strerrormessage, ''doing something'') + '', ''
            + coalesce(@description, '''')
    -- insert into @dir(flags,[key]) select 16,left(@strerrormessage,2000)
    end

if @objitem!=0 exec sp_oadestroy @objitem
if @objfolder!=0 exec sp_oadestroy @objfolder
if @objshellapplication!=0 exec sp_oadestroy @objshellapplication

-- raiserror
if @hr!=0 select @i=cast(left(@strerrormessage,2000) as int)

-- ============================================================== sub folders ==
if @s=0 return

declare cs cursor local for
    select id,case @kp when 0 then @path+[key] else [key] end
    from @dir
    where rid=@rid and flags&32=32
open cs
while 1=1
    begin
    fetch next from cs into @rid,@name
    if @@fetch_status!=0 break

    insert into @dir (rid, flags, [key], dt, n)
    select @rid,flags,[key],dt,n
    from fn__dir(@name,@opt)

    end -- cursor cs
close cs
deallocate cs

return
end -- fn__dir'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__dir:

-- ========================================================== fn__dir_parse_list
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__dir_parse_list',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140110
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__dir_parse_list') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__dir_parse_list') with nowait
        goto skip_fn__dir_parse_list
        end
    if @ver>140110
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__dir_parse_list') with nowait
        goto skip_fn__dir_parse_list
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__dir_parse_list') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__dir_parse_list'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__dir_parse_list]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:dir,list,command,parse,name,date,size,directory
    v:140110\s.zaglio: bug near eng dt (solved but maybe not completelly)
    v:131016.1030\s.zaglio: support and simplify sp__dir
    t:sp__dir ''%temp%\*.*'',@dbg=2
*/
CREATE function fn__dir_parse_list(@line nvarchar(4000),@opt sysname)
returns @t table(
    sdt sysname,
    sfsize sysname null,
    name nvarchar(446)
)
as
begin
declare @max_file_name_size int select @max_file_name_size=446
if @opt=''I''
    --         10        20        30        40        50        60        70        80
    -- 123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.
    -- 14/09/2010  15.34         1.106.500 FILENAME
    -- 25/01/2010  12.27    <DIR>          ADFS
    -- select convert(datetime,''09/01/14 03:30:00PM'')
insert @t(sdt,sfsize,name)
    select
        substring(@line,7,4)+''-''+substring(@line,4,2)+''-''+substring(@line,1,2)
                        +''T''
                        +substring(@line,13,2)+'':''+substring(@line,16,2)+'':00.000''
        as sat,
        ltrim(rtrim(replace(replace(substring(@line,20,16),''.'',''''),'','','''')))
        as sfsize,
        substring(@line,37,@max_file_name_size) [key]

else if @opt=''E''

    --         10        20        30        40        50        60        70        80
    -- 123456789.123456789.123456789.123456789.123456789.123456789.123456789.123456789.
    -- 10/06/2011  04:39 PM    <DIR>          1040
    -- 12/31/2013  02:00 AM               137 tmp_3A2D662D_C46C_425F_8EBA_91635D98C83B
    -- set language english
    -- set language italian
    -- Hijri: dd/mm/yyyy hh:mi:ss:mmmAM: this return a bad year
    -- select convert(sysname,convert(datetime,''04:43:00PM''),8)
    -- xp_cmdshell ''dir %temp%\*.*''
    -- select convert(datetime,''12/24/13 04:43:00PM'')
    insert @t(sdt,sfsize,name)
    select
        /* substring(@line,4,2)+''/''+substring(@line,1,2)+''/''+substring(@line,9,2)
        +'' ''
        +convert(sysname,
                 convert(datetime,
                         substring(@line,13,2)+'':''+
                         substring(@line,16,2)+'':00''+
                         substring(@line,19,2)
                         )
                )
        */
        substring(@line,1,20)   -- 140110\s.zaglio
        as sdt, -- gg/mm/aa hh:mi:ss:mmmAM
        ltrim(rtrim(replace(replace(substring(@line,21,18),''.'',''''),'','',''''))) sfsize,
        substring(@line,40,@max_file_name_size) [key]

return
end -- fn__dir_parse_list'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__dir_parse_list:

-- =========================================================== fn__file_property
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__file_property',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100212
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__file_property') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__file_property') with nowait
        goto skip_fn__file_property
        end
    if @ver>100212
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__file_property') with nowait
        goto skip_fn__file_property
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__file_property') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__file_property'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__file_property]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100212\s.zaglio: return file info
    c:from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=99653
    t:select * from fn__file_property(''c:\boot.ini'')
*/
create function fn__file_property(
    @filename nvarchar (1024)
)
returns @results table (
    errorcode tinyint default (0),
    propname nvarchar (255),
    propvalue sql_variant)
as
begin
declare @oleresult int
declare @fs int
declare @fileid int
declare @message nvarchar (4000)
declare @errorsource nvarchar (255)
declare @errordesc nvarchar (255)
declare @int int
declare @nvarchar nvarchar (1024)
declare @datetime datetime
declare @bigint bigint

-- create an instance of the file system object
execute @oleresult = sp_oacreate ''scripting.filesystemobject'', @fs out
if @oleresult <> 0
begin
exec sp_oageterrorinfo @fs, @errorsource out, @errordesc out

insert @results (errorcode, propname, propvalue)
values (1, @errorsource, @errordesc)

return
end

exec @oleresult = sp_oamethod @fs, ''getfile'', @fileid out, @filename
if @oleresult <> 0
begin
exec sp_oageterrorinfo @fs, @errorsource out, @errordesc out

insert @results (errorcode, propname, propvalue)
values (1, @errorsource, @errordesc)

return
end

exec @oleresult = sp_oagetproperty @fileid, ''attributes'', @int out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''attributes'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''attributes'', @int)

exec @oleresult = sp_oagetproperty @fileid, ''datecreated'', @datetime out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''datecreated'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''datecreated'', @datetime)

exec @oleresult = sp_oagetproperty @fileid, ''datelastaccessed'', @datetime out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''datelastaccessed'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''datelastaccessed'', @datetime)

exec @oleresult = sp_oagetproperty @fileid, ''datelastmodified'', @datetime out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''datelastmodified'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''datelastmodified'', @datetime)

exec @oleresult = sp_oagetproperty @fileid, ''name'', @nvarchar out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''name'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''name'', @nvarchar)

exec @oleresult = sp_oagetproperty @fileid, ''path'', @nvarchar out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''path'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''path'', @nvarchar)

exec @oleresult = sp_oagetproperty @fileid, ''shortpath'', @nvarchar out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''shortpath'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''shortpath'', @nvarchar)

exec @oleresult = sp_oagetproperty @fileid, ''size'', @bigint out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''size'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''size'', @bigint)

exec @oleresult = sp_oagetproperty @fileid, ''type'', @nvarchar out
if @oleresult <> 0
begin
insert @results (errorcode, propname, propvalue)
values (1, ''type'', ''<error retrieving property>'')
end
else
insert @results (propname, propvalue)
values (''type'', @nvarchar)

execute @oleresult = sp_oadestroy @fileid
execute @oleresult = sp_oadestroy @fs

return
end -- function'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__file_property:

-- ========================================================== fn__ftp_parse_list
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__ftp_parse_list',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130603
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__ftp_parse_list') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__ftp_parse_list') with nowait
        goto skip_fn__ftp_parse_list
        end
    if @ver>130603
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__ftp_parse_list') with nowait
        goto skip_fn__ftp_parse_list
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__ftp_parse_list') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__ftp_parse_list'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__ftp_parse_list]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:ftp,list,command,parse,name,date,size,directory,dir,x
    v:130603\s.zaglio: added microsoft and winscp style
    v:130531\s.zaglio: parse line of ls/dir command
    t:
        set language english -- for jan, ...
        select convert(datetime,[timestamp],100) dt,*
        from (
            select ''-- test'' as line
            union select
                ''-rw-r--r--   1 1003     1004       1834147 Apr 20 14:13:06 2012 file''
            union select
                ''05-29-12  03:46PM                  652 file''
            union select
                ''03-26-07  03:16AM       <DIR>          directory''
            union select
                ''-rw-r--r-- 1 ftp ftp           3613 Jan 03  2012 file''
            union select
                ''-rw-r--r--    1 23208    3600        22588 Jan 11 1:43 file''
            union select
                -- probable IBM
                -- Volume Unit     Date  Ext Used Recfm Lrecl BlkSz Dsorg Dsname
                ''APCSPL 3380D  07/16/97  1    1  FB      80  8800  PS  ETC.RPC''
            ) a
        cross apply fn__ftp_parse_list(1,line,default)
*/
CREATE function fn__ftp_parse_list(@lno int,@line nvarchar(4000),@opt sysname)
returns @t table(
    lno int null,
    dir char(1) null,
    [permissions] char(9) null,
    filecode sysname null,
    [owner] sysname null,
    [group] sysname null,
    size int null,
    [timestamp] sysname null,   -- returned as type 100
    name sysname
)
as
begin
/*
^
(?<dir>[\-ld])
(?<permission>([\-r][\-w][\-xs]){3})
\s+
(?<filecode>\d+)
\s+
(?<owner>\w+)
\s+
(?<group>\w+)
\s+
(?<size>\d+)
\s+
(?<timestamp>
 ((?<month>\w{3})\s+(?<day>\d{2})\s+(?<hour>\d{1,2}):(?<minute>\d{2}))
 |
 ((?<month>\w{3})\s+(?<day>\d{2})\s+(?<year>\d{4}))
)
\s+
(?<name>.+)
$
*/
declare
    @s1 int,@s2 int,@s3 int,@s4 int,@s5 int,
    @dts sysname,@tmp sysname,
    @dir char(1),@group sysname,@owner sysname,@size sysname,@name sysname,
    @permissions char(9),@filecode char(1), @done bit

-- strip spaces and mark words positions
select @done=0,@line=ltrim(@line)
while charindex(''  '',@line)>0 select @line=replace(@line,''  '','' '')

select
    @s1=charindex('' '',@line)+1,     @s2=charindex('' '',@line,@s1)+1,
    @s3=charindex('' '',@line,@s2)+1, @s4=charindex('' '',@line,@s3)+1,
    @s4=charindex('' '',@line,@s3)+1, @s5=charindex('' '',@line,@s4)+1

-- if unix style
if @line like ''[-dl][-r][-w][-xs][-r][-w][-xs][-r][-w][-xs] %[0-9]% % [0-9]% %''
    begin

    -- set language english
    -- select convert(datetime,''Jan 11 2012 13:43'',100)
    -- select convert(datetime,''Jan 11 2012 13:43:01'',100)
    -- select convert(datetime,''Jan 11 2012 1:43PM'',100)
    -- select @@language
    -- set language italian

    select
        @dir=left(@line,1),
        @permissions=substring(@line,2,9),
        @filecode=substring(@line,@s1,@s2-@s1),
        @owner=substring(@line,@s2,@s3-@s2),
        @group=substring(@line,@s3,@s4-@s3),
        @size=substring(@line,@s4,@s5-@s4)

    select @tmp=substring(@line,@s5,128)
    select
        @s1=charindex('' '',@tmp)+1,      -- dd
        @s2=charindex('' '',@tmp,@s1)+1,  -- yy
        @s3=charindex('' '',@tmp,@s2)+1,  -- file or hh:mm or year
        @s4=charindex('' '',@tmp,@s3)+1

    -- unify date to "mmm dd yyyy hh:mm[am|pm]"

    -- winscp format "mmm dd hh:mm:ss yyyy "
    if @tmp like ''[a-z][a-z][a-z]%:%:%''
        begin
        select @dts=left(@tmp,@s2-1)
                   +substring(@tmp,@s3,@s4-@s3)
                   +substring(@tmp,@s2,@s3-@s2),
               @name=substring(@tmp,@s4,128),
               @done=1
        end

    -- month day hour:minute
    if @done=0 and @tmp like ''[a-z][a-z][a-z]%:%''
        begin
        select @dts=left(@tmp,@s2-1)
                   +cast(year(getdate()) as char(4))+'' ''
                   +substring(@tmp,@s2,@s3-@s2),
               @name=substring(@tmp,@s3,128),
               @done=1
        end

    if @done=0
        select @dts=left(@tmp,@s3-1),
               @name=substring(@tmp,@s3,128),
               @done=1

    end -- unit style

-- if microsoft style
if @done=0 and @line like ''[0-9][0-9][-/][0-9][0-9][-/][0-9][0-9] %''
    begin

    select
        @dts=convert(
                sysname,
                convert(datetime,substring(@line,1,8),10)
                +convert(datetime,substring(@line,@s1,7),0),
                100),
        @tmp=substring(@line,@s2,@s3-@s2)
    if isnumeric(@tmp)=1
        select
            @dir=''-'',   -- l is linked file
            @size=convert(int,@tmp),
            @name=substring(@line,@s3,128)
    if @tmp=''<DIR>''
        select
            @dir=''d'',
            @size=0,
            @name=substring(@line,@s3,128)
    select @done=1
    end -- microsoft style

if @done=1
    insert @t(
        lno,dir,[permissions],filecode,[owner],[group],size,[timestamp],name
        )
    select
        @lno,
        dir=@dir,
        [permissions]=@permissions,
        filecode=@filecode,
        [owner]=@owner,
        [group]=@group,
        [size]=cast(@size as int),
        [timestamp]=@dts,
        name=@name

return
end -- fn__ftp_list_parse'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__ftp_parse_list:

-- ============================================================== fn__job_status
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__job_status',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131127.1100
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__job_status') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__job_status') with nowait
        goto skip_fn__job_status
        end
    if @ver>131127.1100
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__job_status') with nowait
        goto skip_fn__job_status
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__job_status') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__job_status'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__job_status]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:job,status,history,number,execution
    v:131127.1100\s.zaglio: added name
    v:131126.1000\s.zaglio: added from_date
    v:131122\s.zaglio: modified and integrated into sp__job_status
    r:131117\s.zaglio: return history grouped by number of execution
    t:select * from fn__job_status(''%'',30*24*60,default)
    t:select * from fn__job_status(''%status%'',30*24*60,''fle'')
    t:select * from fn__job_status(''%'',30*24*60,''fle'')
*/
CREATE function fn__job_status(
    @jobs nvarchar(4000),       -- jobs separated by |
    @mins int,                  -- minutes back for search (default 24h)
    @opt sysname
    )
returns @rs table (
    row         int null,
    id          uniqueidentifier not null,
    name        sysname not null,
    run_date    int not null,
    run_time    int not null,
    from_time   int not null,
    dt_ss       int null,
    err         bit null,
    n           int null
    )
as
begin
-- ================================================================== options ==

declare
    -- options
    @fle bit    -- from last error

-- ===================================================================== init ==

select @mins=isnull(@mins,7*24*60)
if not @opt is null
    select
        @opt=''|''+@opt+''|'',
        @fle=charindex(''|fle|'',@opt)
else
    select @fle=0

-- ===================================================================== data ==

declare @data table (
    instance_id int,
    row         int null,
    id          uniqueidentifier not null,
    name        sysname not null,
    stp         int not null,
    err         bit null,
    run_date    int not null,
    run_time    int not null,
    dt_ss int null,
    primary key (instance_id desc)
    )

insert @data(instance_id,row,id,name,stp,err,run_date,run_time,dt_ss)
select
    instance_id,
    row =
        row_number() over(order by sjh.job_id,run_date,run_time,sjh.step_id),
    id = sjh.job_id,
    name = j.name,
    stp = sjh.step_id,
    err = cast(case when sjh.sql_severity>10 then 1 else 0 end as bit),
    run_date,
    run_time,
    dt_ss =
        run_duration%100+(run_duration/100%100*60)+
        (run_duration/10000%100*3600)
-- select top 10 *
from msdb..sysjobhistory sjh (nolock)
join msdb..sysjobs j on j.job_id=sjh.job_id
join fn__str_split(@jobs,''|'') on name like token
and run_date>cast(convert(sysname,dateadd(mi,-@mins,getdate()),112) as int)
and sjh.server=@@servername

/* outcome running job
if 0!=(select top 1 stp from @data)
    insert @data
    select top 1 instance_id+1,row,id,name,0,err,run_date,run_time,dt_ss
    from @data
*/

-- ================================================================= outcomes ==

declare @outcomes table(
    orow        int null,
    row         int null,
    id          uniqueidentifier not null,
    name        sysname not null,
    stp         int not null,
    err         bit null,
    run_date    int not null,
    run_time    int not null,
    dt_ss int null
    )

insert @outcomes
select
    orow=row_number() over(order by row),
    row,id,name,stp,err,run_date,run_time,dt_ss
from @data
where stp=1

-- =================================================================== ranges ==

declare @ranges table(
    rrow int null,
    l    int null,
    r    int null
    )

insert @ranges
select
    rrow=row_number() over(order by a.row),
    a.row as l,
    isnull(b.row-1,(select max(row) from @data d where d.id=a.id)) as r
from @outcomes a
left join @outcomes b on isnull(b.orow,a.orow+1)=a.orow+1

-- ========================================================== groups of steps ==

declare @grps table(
    row         int null,
    id          uniqueidentifier not null,
    err         bit null,
    rrow        int null
    )

insert @grps
select data.row,data.id,data.err,ranges.rrow
from @data data
join @ranges ranges
on data.row between ranges.l and ranges.r

-- ===================================================================== runs ==

declare @runs table(
    row  bigint null,
    id   uniqueidentifier not null,
    err  bit null,
    rrow bigint null
    )

insert @runs
select
    min(row) as row,id,
    cast(sum(cast(err as int)) as bit) err,
    rrow
from @grps
group by id,rrow

-- ========================================================== history summary ==

declare @his table(
    row         bigint null,
    id          uniqueidentifier not null,
    name        sysname not null,
    run_date    int not null,
    run_time    int not null,
    dt_ss int null,
    err         bit null
    )

insert @his(row,id,name,run_date,run_time,dt_ss,err)
select
    row=row_number() over (order by data.row),
    data.id,data.name,data.run_date,data.run_time,data.dt_ss,
    runs.err
from @data data
join @runs runs on data.row=runs.row

-- ================================================================== headers ==

declare @heads table(
    row  bigint null,
    hrow bigint null
    )

insert @heads
select cur.row,hrow = row_number() over (order by cur.row)
from @his cur
left join @his prev on cur.row = prev.row+1
where prev.err != cur.err or prev.id != cur.id or cur.row=1

-- ================================================================= his grps ==

declare @his_grps table(
    row         int null,
    id          uniqueidentifier not null,
    run_date    int not null,
    run_time    int not null,
    dt_ss int null,
    err         bit null,
    hrow        bigint null
    )

insert @his_grps(row,id,run_date,run_time,dt_ss,err,hrow)
select  his.row,his.id,his.run_date,his.run_time,his.dt_ss,his.err,
        hrow = (select max(hrow)
                from @heads heads
                where heads.row <= his.row)
from @his his

-- =============================================================== record set ==

if @fle=0
    insert @rs(row,id,name,run_date,run_time,from_time,dt_ss,err,n)
    select
        his.row,his.id,his.name,
        his.run_date,his.run_time,err.run_time,his.dt_ss,
        his.err,err.n
    from @his his
    join (
        select max(row) row,err,count(hrow) n,min(run_time) run_time
        from @his_grps
        group by hrow,err
        ) err on his.row=err.row
    order by row desc
else
    insert @rs(row,id,name,run_date,run_time,from_time,dt_ss,err,n)
    select
        his.row,his.id,his.name,his.run_date,
        his.run_time,err.run_time,his.dt_ss,
        his.err,err.n
    from @his his
    join (
        select max(row) row,err,count(hrow) n,min(run_time) run_time
        from @his_grps
        group by hrow,err
        ) err on his.row=err.row
    join (
        select id,max(row) row
        from @his_grps
        where err=1
        group by id,err
        ) fle on his.id=fle.id and his.row>=fle.row
    order by row desc

return
end -- fn__job_status'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__job_status:

-- ========================================================== fn__ntext_to_lines
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__ntext_to_lines',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110721.1646
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__ntext_to_lines') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__ntext_to_lines') with nowait
        goto skip_fn__ntext_to_lines
        end
    if @ver>110721.1646
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__ntext_to_lines') with nowait
        goto skip_fn__ntext_to_lines
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__ntext_to_lines') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__ntext_to_lines'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__ntext_to_lines]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    g:utility
    v:110721.1646\s.zaglio: alternative use of sp__write_ntext_to_lines
    t:
        declare @obj sysname,@le bit
        select @le=1,@obj=
            ''SP_SAP_EXPORT_PRODUCTIONS_test_bug''
            -- ''sp__write_ntext_to_lines''
        select *,charindex(char(13),line) pcr,charindex(char(10),line) plf
        from fn__ntext_to_lines(
                (select top 1 [text]
                    from syscomments
                    where id=object_id(@obj)
                    )
                ,@le  -- @leave_le
            )
        order by lno
*/
CREATE function fn__ntext_to_lines(@blob ntext,@leave_le bit)
returns @t table(lno int identity primary key,line nvarchar(4000))
as
begin

declare
    @drop bit,
    @i int,@j int,@l int,@n int,@p int,
    @ncrlf nvarchar(2),@cr nchar(1),@lf nchar(1),
    @lcrlf int,@dbg bit

select @dbg=0

declare @lines table (pos int primary key,leng int)

select @ncrlf=crlf,@cr=cr,@lf=lf,@leave_le=isnull(@leave_le,0)
from fn__sym()

-- identify row separator
select
    @i=charindex(@cr,@blob),
    @j=charindex(@lf,@blob),
    @n=datalength(@blob)/2
/*
if @dbg=1
    insert @t(line)
    select ''@i=''+cast(@i as sysname) union
    select ''@j=''+cast(@j as sysname) union
    select ''@n=''+cast(@n as sysname)
*/

if @i is null return
if @i=0 and @j=0 and @n<4000
    begin
    insert @t(line) select substring(@blob,1,4000)
    return
    end

if @i>0 and @j=0 select @ncrlf=@cr
if @i=0 and @j>0 select @ncrlf=@lf
if @i=@j+1 select @ncrlf=@lf+@cr
select @lcrlf=len(@ncrlf)
/*
if @dbg=1
    insert @t(line)
    select ''@lcrlf=''+cast(@lcrlf as sysname) union
    select ''@cr=''+cast(unicode(@cr) as sysname) union
    select ''@lf=''+cast(unicode(@lf) as sysname) union
    select ''@ncrlf=''+cast(unicode(substring(@ncrlf,1,1)) as sysname)+''|''+
                     cast(isnull(unicode(substring(@ncrlf,2,1)),''0'') as sysname)
*/
select top 1 @i=1,@p=1,@j=1

while 1=1
    begin
    select top 1 @i=charindex(@ncrlf,substring(@blob,@j,4000))

    if @i=0
        begin
        -- last pieces that do not end with crlf
        -- if @dbg=1 exec sp__printf ''j:%d, i:%d, @n:%d'',@j,@i,@n
        if @n>@j/*+@lcrlf*/-1 insert @lines select @j,@n-@j+1
        break
        end
    -- if @dbg=1 exec sp__printf ''j:%d, i:%d'',@j,@i
    if @leave_le=1 select @l=@i+@lcrlf-1 else select @l=@i-1
    insert @lines select @j,@l
    /*
    if @dbg=1
        begin
        insert @t(line)
        select left(''i:''+cast(@i as sysname)+
                    '';j:''+cast(@j as sysname)+
                    '';l:''+cast(@l as sysname)+
                    '';                    '',20)+
               replace(replace(substring(@blob,@j,4000),@lf,''|''),@cr,''+'')
        insert @t(line) select space(@i+20-1)+''^''
        end
    */
    select @j=@j+@i+@lcrlf-1
    end -- while

/*
insert @t(line)
select ''pos:''+cast(pos as sysname)+''; leng:''+cast(leng as sysname)
from @lines
return
*/
insert @t(line)
select substring(@blob,pos,leng) line
from @lines

return
end -- fn__ntext_to_lines'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__ntext_to_lines:

-- =============================================================== fn__parse_url
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__parse_url',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130906.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__parse_url') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__parse_url') with nowait
        goto skip_fn__parse_url
        end
    if @ver>130906.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__parse_url') with nowait
        goto skip_fn__parse_url
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__parse_url') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__parse_url'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__parse_url]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    d:130906\s.zaglio:fn__parseurl
    v:130906.1000\s.zaglio:moved out tests,renamed and + parse of local path
    v:120125\s.zaglio:done
    r:120124\s.zaglio:working
    r:120123\s.zaglio:parse the unified resource locator
    d:120123\s.zaglio:fn__parsefilename
    t:sp__parse_url_test
*/
CREATE function [dbo].[fn__parse_url](
    @url nvarchar(4000),
    @opt sysname
    )
returns @t table(
    url nvarchar(4000) null,
    normalized nvarchar(4000) null,
    protocol sysname null,
    [uid] sysname null,
    [pwd] sysname null,
    host sysname null,
    port sysname null,              -- because can contain the drive/device
    [path] nvarchar(4000) null,
    page nvarchar(4000) null,
    query nvarchar(4000) null,
    dbg sysname null
    )
as
begin
declare
    @dsp as int,                -- double slash position is the fulcrum
    @nurl nvarchar(4000),       -- normalized url
    @ourl nvarchar(4000),       -- original url
    @protocol sysname,
    @uid sysname,
    @pwd sysname,
    @host sysname,
    @port sysname,
    @path nvarchar(4000),
    @page nvarchar(4000),
    @query nvarchar(4000),
    @puid int,@ppwd int,@ppath int,@pquery int,
    @pport int,@ppage int,@lpath int,
    @psep nvarchar(2),@revert bit,
    @dbg sysname

if @url is null goto ret

-- normalize name
select
    @ourl=@url,
    @url=replace(@url,''%20'','' ''),
    @psep=psep
from fn__sym()

-- reduce firefox long file url
select @url=replace(@url,''/////'',''//'')

-- special check for local path
if @url like ''[a-z]:\[^\]%''
    select @url=''file:///''+@url,@revert=1
else
    select @revert=0


-- dsp must be specified
select
    @nurl=replace(@url,''\'',''/''),
    @dsp=charindex(''//'',@nurl),
    @ppath=charindex(''/'',@nurl,@dsp+2),
    @pquery=charindex(''?'',@nurl,@dsp+2)
if @dsp = 0 goto wrong
if @ppath = 0 select @ppath=len(@nurl)+1     -- ftp path can not have it
if @pquery=0 select @pquery=len(@nurl)+1

-- select * from dbo.fn__parse_url(''c:\share\dir\file.ext'',default)

select
    @protocol=left(@url,@dsp-1),
    @host=substring(@url,@dsp+2,@ppath-@dsp-2),
    @path=substring(@url,@ppath,@pquery-@ppath),
    @nurl=reverse(substring(@nurl,@ppath,@pquery-@ppath)),    -- used below
    @query=substring(@url,@pquery+1,4000),
    @ppwd=charindex('':'',@host),
    @puid=charindex(''@'',@host)
    -- ,@dbg=@nurl

if @revert=1 select @path=replace(@path,''/'',''\'')

if @protocol!=''''
    begin
    if right(@protocol,1)!='':'' goto wrong
    else select @protocol=left(@protocol,@dsp-2)
    end

if @ppwd>0
    select
        @pwd=left(@host,@ppwd-1),
        @uid=substring(@host,@ppwd+1,@puid-@ppwd-1),
        @host=substring(@host,@puid+1,128),
        @pport=charindex('':'',@host)

if @pport>0
    begin
    select
        @port=substring(@host,@pport+1,16),
        @host=left(@host,@pport-1)
    if isnumeric(@port)=0 and @protocol!=''file''
        goto wrong      -- or is the case to cause and error? No.
                        -- the parse check the syntax not the semantic
    end

-- parse page name if exists
select
    @lpath=len(@path),
    @ppath=@lpath-charindex(''/'',@nurl)+1,
    @ppage=charindex(''.'',@nurl)
    -- ,@dbg=@nurl

if @ppage>0 select @ppage=@lpath-@ppage+1

if @ppage>@ppath and @ppath>0
    select
        @page=substring(@path,@ppath+1,512),
        @path=left(@path,@ppath)
else
    select
        @page=''''

if @protocol=''file'' and @host=''''
    select
        @host=null,
        @port=substring(@path,2,charindex('':'',@path)-1),
        @path=substring(@path,charindex('':'',@path)+1,512)

insert @t(  url,        normalized,
            protocol,   [uid],  [pwd],  host,
            port,       [path], page,   query,
            dbg
         )
select      @ourl,      @url,
            @protocol,  @uid,   @pwd,   @host,
            @port,      @path,  @page,  @query,
            @dbg

ret:
return
wrong:
insert @t(  url,        normalized,
            protocol,   [uid],  [pwd],  host,
            port,       [path], page,   query,
            dbg
         )
select      @ourl,      null,
            null,       null,   null,   null,
            null,       null,   null,   null,
            null
return
end -- fn__parse_url'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__parse_url:

-- =============================================================== fn__parsename
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__parsename',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100612
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__parsename') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__parsename') with nowait
        goto skip_fn__parsename
        end
    if @ver>100612
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__parsename') with nowait
        goto skip_fn__parsename
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__parsename') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__parsename'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__parsename]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100612\s.zaglio: revision
    r:100404\s.zaglio: returns tsql name splitted
    t:select * from fn__parsename(''svr.db..obj'',0,0)
    t:select * from fn__parsename(''[db]..obj'',0,0)
    t:select * from fn__parsename(''*obj*'',0,1)
    t:select * from fn__parsename(''*obj*'',1,1)
*/
CREATE function [dbo].[fn__parsename](@obj sysname,@quoted bit=0,@defaults bit=0)
returns @t table(svr sysname null,db sysname null,sch sysname null,obj sysname null)
--begin
as
begin
if @obj is null return
declare @svr sysname,@db sysname,@sch sysname,@tbl sysname
select
    @svr=parsename(@obj,4),
    @db=parsename(@obj,3),
    @sch=parsename(@obj,2),
    @tbl=parsename(@obj,1)

if @defaults=1
    select
        @svr=coalesce(@svr,dbo.fn__servername(Null)),
        @db=coalesce(@db,db_name()),
        @sch=coalesce(@sch,''dbo'')

if @quoted=1
    select
        @svr=quotename(@svr),
        @db=quotename(@db),
        @sch=quotename(@sch),
        @tbl=quotename(@tbl)

insert @t select @svr,@db,@sch,@tbl
return
end -- fn__parsename'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__parsename:

-- =================================================================== fn__place
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__place',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121118
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__place') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__place') with nowait
        goto skip_fn__place
        end
    if @ver>121118
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__place') with nowait
        goto skip_fn__place
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__place') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__place'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__place]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:street, place, civic, split, correc, normalize
    v:121118\s.zaglio: working
    v:121007\s.zaglio: split and normalize an address
    t:
        select *
        from tst_street_tbl t
        cross apply fn__place(t.addr,default) p
        cross apply fn__place(t.addr,0) d      -- debug version
        where 1=1
        -- and t.addr=''STR. COMUNALE MARANDA 9''
        and t.addr like ''%riposo%''
        and p.typ_id is null
*/
CREATE function fn__place(@address sysname,@lng tinyint)
returns @t table (
    typ nvarchar(32), typ_id tinyint,
    name nvarchar(128),
    num nvarchar(64)
    -- ,co nvarchar(64)
    )
as
begin
-- declare @addr sysname
declare
    @p_name int,@p_num int,@l int,@p_co int,@p_via int,
    @typ nvarchar(32), @name nvarchar(128), @num nvarchar(64),
    @co nvarchar(64), @typ_id int,@i int,@addr sysname,@j int,
    @c nchar,@pc nchar, @co_id int

-- replace not good symbols: ltrim, rtrim
select @i=1,@l=len(@address),@addr='''',@j=0,@pc='' '',@p_via=0
while @i<=@l
    begin
    select @c=substring(@address,@i,1),@i=@i+1
    -- skip initial spaces
    if (@c=N'' '' and @j=0) continue
    if @c in (''-'','':'') select @c=''.''
    else if @c in (''('','')'','','') select @c='' ''
    -- insert @t(typ,typ_id,name,num) select @pc+''|''+@c+''|'',@i,@addr,@j
    if @pc!='' '' select @addr=@addr+@pc,@j=@j+1
    if @c='' '' and not @pc in (''.'','' '') select @addr=@addr+@c,@j=@j+1
    select @pc=@c
    end
if @c!='' '' select @addr=@addr+@c
-- insert @t(typ,typ_id,name,num) select @pc+''|''+@c+''|'',@i,@addr,@j return

select @addr=replace(replace(@addr,''Rif..'',''''),''Rif.'','''')

-- special cases of address inversion
select top 1 @p_via=charindex(cod,@addr) ,@typ_id=id
-- select *
from fn__place_type()
where charindex(cod,@addr)>0
select top 1 @p_co=charindex(cod,@addr),@co_id=id
-- select *,charindex(cod,''PRESSO CLINICA SAN RAFFAELE'') p_co
from fn__place_at()
where charindex(cod,@addr)>0
select @p_co=isnull(@p_co,0)
if @p_co=1 and @p_via>1
    select @addr=substring(@addr,@p_via,128)+'' ''
                +left(@addr,@p_via-1)

select @l=len(@addr)
if @l<3 goto store

select @p_name=charindex('' '',@addr)
if @p_via=0 and @p_name>0 select @typ=left(@addr,@p_name),@p_name=1

select @p_num=patindex(''%KM%[0-9]%'',@addr)

if @p_num=0 select @p_num=patindex(''%N.[0-9]%'',@addr)

if @p_num=0 select @p_num=patindex(''%N°[0-9]%'',@addr)

if @p_num=0
    begin
    select @p_num=patindex(''%[0-9] %'',reverse(@addr))
    if @p_num>0 select @p_num=@l-@p_num+1
    end

-- if @p_name=0 select @p_name=1

-- select * from arc04_addresses cross apply fn__street(pla)

if not @lng is null
    begin
    insert @t(typ,typ_id,name,num)
    select @p_via,@p_co,@p_name,@p_num
    return
    end

-- if @p_name=0 select @p_name=1
-- if @p_co=0  select @p_co =@l+1
if @p_name>0
    begin
    if @typ is null
        select @typ=ltrim(left(@addr,@p_name-1)),@p_name=@p_name+1 -- skip space
    if @p_num=0 and @p_co=0
        select @name=ltrim(rtrim(substring(@addr,@p_name,abs(@l+1-@p_name))))
    else
        begin
        if @p_num=0 and @p_co>0
            select
                @num=ltrim(rtrim(replace(substring(@addr,@p_co,@l+1-@p_co),'','',''''))),
                @name=ltrim(rtrim(substring(@addr,@p_name,abs(@p_co-@p_name))))
        else
            select
                @num=ltrim(rtrim(replace(substring(@addr,@p_num,@l+1-@p_num),'','',''''))),
                @name=ltrim(rtrim(substring(@addr,@p_name,abs(@p_num-@p_name))))
        end
    end
else
    select @name=@addr

select @name=replace(@name,'','','''')
-- if right(@name,2) in (''n.'','' n'',''n°'') select @name=left(@name,len(@name)-2)

store:
-- exec sp__printf ''pn:%d n:%s'',@p_num,@name

if @name is null
    insert @t select null,@typ_id,@addr,null--,@co
else
    insert @t select @typ,@typ_id,@name,@num--,@co

return
end -- fn__place'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__place:

-- ============================================================== fn__plan_dates
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__plan_dates',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121118
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__plan_dates') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__plan_dates') with nowait
        goto skip_fn__plan_dates
        end
    if @ver>121118
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__plan_dates') with nowait
        goto skip_fn__plan_dates
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__plan_dates') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__plan_dates'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__plan_dates]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,plan
    k:plan,calendar,planning
    r:121118\s.zaglio: convert plan calendar structure into dates
    t:sp__plan_calendar_test
*/
CREATE function fn__plan_dates(
    @year int,
    @days nvarchar(24)    -- 12 int:bit0=01/01
    )
returns @t table (dt datetime)
as
begin
/*
    declare @t table (dt datetime)
    declare @year int,@days varchar(48)
    select @year=2012,@days=cast(1 as varchar(48))
    select cast(@days as varbinary(48))
*/
declare @pmm int,@chunk binary(4),@dt datetime,@dayp2 bigint,@day int,@month int
select
    @dt=0,
    @dt=dateadd(yy,@year-year(0),@dt),
    @month=0,
    @pmm=1

-- select * from dbo.fn__plan_dates(2012,cast(1 as varchar(48)))

while @pmm<=len(@days)
    begin
    select @chunk=cast(substring(@days,@pmm,2) as binary(4))
    select @day=0,@dayp2=1
    while @day<31
        begin
        /*
        exec sp__printf
                ''chunk:%d, pmm:%d, day:%d, dp2:%d'',
                @chunk,@pmm,@day,@dayp2
        */
        if @chunk&@dayp2=@dayp2 insert @t select dateadd(mm,@month,@dt)+@day
        select @day=@day+1,@dayp2=@dayp2*2
        end
    select @pmm=@pmm+2,@month=@month+1
    end

-- select * from @t

return
end -- fn__plan_dates'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__plan_dates:

-- =============================================================== fn__regexfind
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__regexfind',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131009
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__regexfind') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__regexfind') with nowait
        goto skip_fn__regexfind
        end
    if @ver>131009
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__regexfind') with nowait
        goto skip_fn__regexfind
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__regexfind') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__regexfind'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__regexfind]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131009\s.zaglio:restyle
    v:080101\www.simple-talk.com
    t:
        begin
        --showing the context where two words ''for'' and ''last'' are found in proximity
        DECLARE @sample nvarchar(2000)
        SELECT @Sample=''You have failed me for the last time, Admiral.
        We have not long to wait for your last gasp''
        SELECT ''...''+SUBSTRING(@Sample,Firstindex-8,length+16)+''...''
            FROM dbo.fn__RegexFind (''\bfor(?:\W+\w+){0,3}?\W+last\b'',
                   @sample,1,1)

        --finding repeated words, showing the repetition and the repeated word
        SELECT [repetition]=value, [word]=SubmatchValue FROM dbo.fn__RegexFind (''\b(\w+)\s+\1\b'',
        ''this this is is a repeated word word word'',1,1)

        --Split lines based on a regular expression
        SELECT value FROM dbo.fn__regexfind(''[^\r\n]*(?:[\r\n]*)'',
        ''
        This is the second line
        This is the third
        and the fourth'',1,1) WHERE length>0

        --break up all words in a string into separate table rows
        SELECT value FROM dbo.fn__RegexFind (''\b[\w]+\b'',
        ''Hickory dickory dock,the mouse ran up the clock'',1,1)

        --split ntext into keywords and values
        SELECT Match_ID,
        [keyword]=MAX (CASE WHEN submatch_ID=1 THEN  submatchValue ELSE '''' END),
        [value]=MAX (CASE WHEN submatch_ID=2 THEN  submatchValue ELSE '''' END)
          FROM dbo.fn__RegexFind (''(\w+)\s*=\s*(.*)\s*'',
        ''firstname=Phil
        Lastname=Factor
        Salary=$200,000
        age=unknown to us
        Post=DBA'',1,1) GROUP BY Match_ID

        SELECT * FROM dbo.fn__RegexFind (''([^\|\r\n]+[\|\r\n]+)'',
        ''1|white gloves|2435|24565
        2|Sports Shoes|285678|0987
        3|Stumps|2845|987
        4|bat|29862|4875'',1,1)

        --get valid dates and convert to SQL Server format
        SELECT DISTINCT CONVERT(DATETIME,value,103)
        FROM dbo.fn__RegexFind (''\b(0?[1-9]|[12][0-9]|3[01])[- /.](0?[1-9]|1[012])[- /.](19|20?[0-9]{2})\b'',''
        12/2/2006 12:30 <> 13/2/2007
        13:30
        32/3/2007
        2-4-2007
        25.8.2007
        1/1/2005
        34/2/2104
        2/5/2006'',1,1)

        end
*/
CREATE function fn__regexfind(
    @pattern nvarchar(255),
    @matchstring nvarchar(max),
    @global bit = 1,
    @multiline bit =1)
returns
    @result table
        (
        match_id int,
          firstindex int ,
          length int ,
          value nvarchar(2000),
          submatch_id int,
          submatchvalue nvarchar(2000),
         error nvarchar(255)
        )


as -- columns returned by the function
/* this is the most powerful function for doing complex finding and replacing of text.
as it passes back detailed records of the hits, including the location and the backreferences,
it allows for complex manipulations.

this is written as a table function. the regex routine actually passes back a collection for each ''hit''.
in the relational world, you''d normally represent this in two tables, so we''ve returned a left outer join
of the two logical tables so as to pass back all the information.
this seems to cater for all the uses we can think of. we also append an error column, which should be blank!
*/
begin
declare @objregexexp int,
    @objerrorobject int,
    @objmatch int,
    @objsubmatches int,
    @strerrormessage nvarchar(255),
   @error nvarchar(255),
    @substituted nvarchar(4000),
    @hr int,
    @matchcount int,
    @submatchcount int,
    @ii int,
    @jj int,
    @firstindex int,
    @length int,
    @value nvarchar(2000),
    @submatchvalue nvarchar(2000),
    @objsubmatchvalue int,
    @command nvarchar(4000),
    @match_id int

declare @match table
    (
      match_id int identity(1, 1)
                   not null,
      firstindex int not null,
      length int not null,
      value nvarchar(2000)
    )
declare @submatch table
    (
      submatch_id int identity(1, 1),
      match_id int not null,
      submatchno int not null,
      submatchvalue nvarchar(2000)
    )

select  @strerrormessage = ''creating a regex object'',@error=''''
exec @hr= sp_oacreate ''vbscript.regexp'', @objregexexp out
if @hr = 0
    select  @strerrormessage = ''setting the regex pattern'',
            @objerrorobject = @objregexexp
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''pattern'', @pattern
if @hr = 0
    select  @strerrormessage = ''specifying a case-insensitive match''
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''ignorecase'', 1
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''multiline'', @multiline
if @hr = 0
    exec @hr= sp_oasetproperty @objregexexp, ''global'', @global
if @hr = 0
    select  @strerrormessage = ''doing a match''
if @hr = 0
    exec @hr= sp_oamethod @objregexexp, ''execute'', @objmatch out,
        @matchstring
if @hr = 0
    select  @strerrormessage = ''getting the number of matches''
if @hr = 0
    exec @hr= sp_oagetproperty @objmatch, ''count'', @matchcount out
select  @ii = 0
while @hr = 0
    and @ii < @matchcount
    begin
    /*the match object has four read-only properties.
    the firstindex property indicates the number of characters in the string to the left of the match.
    the length property of the match object indicates the number of characters in the match.
    the value property returns the ntext that was matched.*/
        select  @strerrormessage = ''getting the firstindex property'',
                @command = ''item('' + cast(@ii as nvarchar) + '').firstindex''
        if @hr = 0
            exec @hr= sp_oagetproperty @objmatch, @command,
                @firstindex out
        if @hr = 0
            select  @strerrormessage = ''getting the length property'',
                    @command = ''item('' + cast(@ii as nvarchar) + '').length''
        if @hr = 0
            exec @hr= sp_oagetproperty @objmatch, @command, @length out
        if @hr = 0
            select  @strerrormessage = ''getting the value property'',
                    @command = ''item('' + cast(@ii as nvarchar) + '').value''
        if @hr = 0
            exec @hr= sp_oagetproperty @objmatch, @command, @value out
        insert  into @match
                (
                  firstindex,
                  [length],
                  [value]
                )
                select  @firstindex + 1,
                        @length,
                        @value
        select  @match_id = @@identity
        /* the submatches property of the match object is a collection of
        strings. It will only hold values if your regular expression has
        capturing groups. The collection will hold one string for each
        capturing group. The count property (returned as submatchcount)
        indicates the number of string in the collection.
        The item property takes an index parameter, and returns the ntext
        matched by the capturing group.
        */
        if @hr = 0
            select  @strerrormessage = ''getting the submatches collection'',
                    @command = ''item('' + cast(@ii as nvarchar)
                    + '').submatches''
        if @hr = 0
            exec @hr= sp_oagetproperty @objmatch, @command,
                @objsubmatches out
        if @hr = 0
            select  @strerrormessage = ''getting the number of submatches''
        if @hr = 0
            exec @hr= sp_oagetproperty @objsubmatches, ''count'',
                @submatchcount out
        select  @jj = 0
        while @hr = 0
            and @jj < @submatchcount
            begin
                if @hr = 0
                    select  @strerrormessage = ''getting the submatch value property'',
                            @command = ''item('' + cast(@jj as nvarchar)
                            + '')'' ,@submatchvalue=null
                if @hr = 0
                    exec @hr= sp_oagetproperty @objsubmatches, @command,
                        @submatchvalue out
                insert  into @submatch
                        (
                          match_id,
                          submatchno,
                          submatchvalue
                        )
                        select  @match_id,
                                @jj+1,
                                @submatchvalue
                select  @jj = @jj + 1
            end
        select  @ii = @ii + 1
    end
if @hr <> 0
    begin
        declare @source nvarchar(255),
            @description nvarchar(255),
            @helpfile nvarchar(255),
            @helpid int

        execute sp_oageterrorinfo @objerrorobject, @source output,
            @description output, @helpfile output, @helpid output
        select  @error = ''error whilst ''
                + coalesce(@strerrormessage, ''doing something'') + '', ''
                + coalesce(@description, '''')
    end
exec sp_oadestroy @objregexexp
 exec sp_oadestroy        @objmatch
 exec sp_oadestroy        @objsubmatches

insert into @result
      (match_id,
      firstindex,
      [length],
      [value],
      submatch_id,
      submatchvalue,
     error)


select  m.[match_id],
       [firstindex],
       [length],
       [value],[submatchno],
       [submatchvalue],@error
from    @match m
left outer join   @submatch s
on m.match_id=s.match_id
if @@rowcount=0 and len(@error)>0
insert into @result(error) select @error
return
end -- fn__regexfind'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__regexfind:

-- =============================================================== fn__schema_of
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__schema_of',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100328
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__schema_of') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__schema_of') with nowait
        goto skip_fn__schema_of
        end
    if @ver>100328
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__schema_of') with nowait
        goto skip_fn__schema_of
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__schema_of') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__schema_of'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__schema_of]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100328\s.zaglio: return a table with id
    v:100204\s.zaglio: return schema of a table
    t:print dbo.fn__schema_of(''fn__schema_of'')
*/
create function [dbo].[fn__schema_of](@table sysname)
returns @t table ([name] sysname,id int)
as
begin
-- sp__find ''fn__schema_of'' -> fn__comment_types,sp__script
declare @schema sysname
insert @t
select u.name,u.uid
from sysobjects o
join sysusers u on o.uid=u.uid
where o.id=object_id(@table)
return
end -- fn__schema_of'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__schema_of:

-- ===================================================== fn__script_group_select
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__script_group_select',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130922.1101
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__script_group_select') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__script_group_select') with nowait
        goto skip_fn__script_group_select
        end
    if @ver>130922.1101
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__script_group_select') with nowait
        goto skip_fn__script_group_select
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__script_group_select') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__script_group_select'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__script_group_select]

begin try
exec dbo.sp_executesql @statement = N'/*  Leave this unchanged doe MS compatibility
    l:see LICENSE file
    g:utility,script
    d:130922\s.zaglio:fn__object_id
    v:130922.1101\s.zaglio:added lcl and replaced fn__str_parse
    v:130905,130730.1225,130726\s.zaglio:top0;wider nvarchar;a bug when empty group
    r:130708,130703,\s.zaglio: added exclusions;set of core
    r:130518\s.zaglio: select objects for scripting purpose
    t:
        select *
        from fn__script_group_select(''utility'',default,default,default,default)
        order by ord
    t:select * from fn__script_group_select(''utility'',default,default,''@s.zaglio'',default)
    n:https://code.google.com/p/jquerycsvtotable/
    n:http://tablesorter.com/docs/
*/
CREATE function fn__script_group_select(
    @grp        sysname = null,
    @exclude    nvarchar(4000) = null,
    @include    nvarchar(4000) = null,
    @opt        varchar(256) = null,
    @dbg        int = 0
    )
returns @grp_objs table (
    obj_id int null,
    obj sysname,
    xt nvarchar(2) null,
    tag nvarchar(16) default(''v''),
    aut sysname null,
    ver nvarchar(16) null,
    [des] sysname null,
    grp1 sysname null default(''''),
    grp2 sysname null default(''''),
    grp3 sysname null default(''''),
    core bit default(0),                -- basic obj used to define others
    [drop] nvarchar(512) null,
    lcl sysname null,                   -- previous description or last change log
    ord int,
    match bit
    )
as
begin

declare
    @grp_setup sysname,@aut sysname

select @opt=dbo.fn__str_quote(coalesce(@opt,''''),''|'')

if charindex(''|top0|'',@opt)>0 return

if charindex(''|@'',@opt)>0
    select @aut=dbo.fn__str_between(@opt,''|@'',''|'',default)

-- ============================================================= declarations ==

declare @crlf nvarchar(4)

declare @info table (
    obj_id int,obj sysname,tag nvarchar(4),xt varchar(2),
    val1 nvarchar(4000),val2 nvarchar(4000),val3 nvarchar(4000)
    )

declare @excludes table (obj sysname)

-- ===================================================================== body ==

select
    @crlf=crlf,
    @grp_setup=(select top 1 name
                from sys.objects
                where type=''P'' and name like ''%''+@grp+''_setup''
               ),
    @exclude=isnull(@exclude+''|'','''')
            +''tr__script_trace|tr__script_trace_db|%[_]old''
from fn__Sym()

insert @excludes select token
from dbo.fn__str_table(@exclude,''|'')

-- list all revisioned, groups objects info
insert @info(obj_id,obj,xt,tag,val1,val2,val3)
select
    si.obj_id,si.obj,si.xt,si.tag,
    isnull(si.val1,''''),isnull(si.val2,''''),isnull(si.val3,'''')
-- select *
from dbo.fn__script_info(null,''rvg'',0) si

-- removed excluded
delete nfo from @info nfo join @excludes ex on nfo.obj like ex.obj

-- apply group filter
delete obj
from @info obj
where obj.tag=''g'' and not @grp in (obj.val1,obj.val2,obj.val3)

-- insert remaining revisions with grp info
insert into @grp_objs(
    obj_id,obj,xt,tag,ver,aut,
    des,grp1,grp2,grp3
    )
select
    vr.obj_id,vr.obj,vr.xt,vr.tag,left(vr.val1,16) as ver,vr.val2 as aut,
    vr.val3, g.val1,g.val2,g.val3
from @info vr,@info g
where vr.tag in (''r'',''v'') and g.tag=''g'' and vr.obj_id=g.obj_id
and (@aut is null or vr.val2=@aut)

-- insert @grp_objs(obj,aut) select ''qui'',@aut goto ret

-- delete group info to avoid bad next joins
delete @info where tag=''g''

-- deprecated of selected objects
insert into @grp_objs(obj_id,obj,tag,aut,ver,des)
select
    d.obj_id,d.obj,d.tag,d.val2,left(d.val1,16) as ver,
    case when isnumeric(d.val1)=1 then d.val3 else d.val1 end as des
-- select *
from fn__script_info(default,''d'',default) d
join @grp_objs o on d.obj_id=o.obj_id

-- remove deprecated from objects to script if deprecated date is >
delete vr
from @grp_objs vr
join @grp_objs d on vr.obj=d.des
where vr.tag in(''v'',''r'') and d.tag=''d''
-- and isnumeric(d.ver)=1 and d.ver>vr.ver

-- ============================================================ set the order ==

update objs set
    core=case when core.obj is null then 0 else 1 end,
    ord=
        case
        when not core.obj is null
        then core.ord
        else
            case
            when xt=''TD'' then 90                 -- trigger db
            when objs.obj=@grp_setup then 95     -- eXecute tag
            when xt=''U''  then 100
            when xt=''FN'' then 200
            when xt=''IF'' then 210
            when xt=''TF'' then 220
            when xt=''V''  then 300
            else 999
            end
        end,
    [drop]= case tag
            when ''d'' then null
            else so.if_exists+@crlf+''    ''+so.drop_script
            end
from @grp_objs objs
left join (
    select ''fn__script_sign'' obj,   189 as ord  union   -- must be 1st
    select ''tids'',                  192         union
    select ''flags'',                 195         union
    select ''fn__sym'',               198         union
    select ''fn__str_parse'',         201         union
    select ''fn__str_parse_table'',   204         union
    select ''fn__script_info_tags'',  207         union
    select ''fn__script_info'',       210         union
    select ''fn__script_drop'',       213         union
    select ''fn__sysobjects'',        216         union
    select ''fn__script_sysobjs'',    219         union
    select ''fn__str_distance'',      222         union
    select ''sp__deprecate'',         225         union
    select ''sp__script_update'',     228
    ) core on objs.obj=core.obj
join fn__sysobjects(default,default,''drop_script|if_exists|relaxed'') so
on objs.obj_id=so.id

if charindex(''|lcl|'',@opt)>0
    update objs set lcl=lcl.val3
    from @grp_objs objs
    left join dbo.fn__script_info(null,''rv'',1) lcl
    on objs.obj=lcl.obj
    where objs.tag in (''r'',''v'')
/*
    t:
        select *
        from fn__script_group_select(''utility'',default,default,''lcl'',default)
        order by ord
*/
ret:
return
end -- fn__script_group_select'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__script_group_select:

-- ============================================================ fn__sql_def_cols
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__sql_def_cols',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110601
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__sql_def_cols') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__sql_def_cols') with nowait
        goto skip_fn__sql_def_cols
        end
    if @ver>110601
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__sql_def_cols') with nowait
        goto skip_fn__sql_def_cols
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__sql_def_cols') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__sql_def_cols'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__sql_def_cols]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110601\s.zaglio: added columns def
    v:100404\s.zaglio: list simples create table columns
    t:
        create table test_cols(
            a int, b sysname,c bit,d numeric(4,2),
            e varchar(3),f nvarchar(2),
            u uniqueidentifier
            )
        select * into #test from test_cols
        select * from fn__sql_def_cols(''test_cols'',default,default)
        select * from fn__sql_def_cols(''test_cols'',default,''b,d'')
        select * from fn__sql_def_cols(''#test'',default,default)
        drop table #test
        drop table test_cols
    t:select * from systypes order by [type]
*/
CREATE function [dbo].[fn__sql_def_cols](
    @tbl sysname,
    @sep nvarchar(32)='','',
    @excludes nvarchar(4000)=null
    )
returns @cols table (
    fld sysname,def sysname,ord int,sep nchar(1),
    [type] sysname,[prec] nvarchar(32),[scale] nvarchar(32),
    gtype nvarchar(2)   -- N=numeric, S=string, B=binary, V=variant
    )
as
begin
declare @ex table (fld sysname)
if not @excludes is null
    insert @ex
    select token
    from dbo.fn__str_table(@excludes,@sep)

if left(@tbl,1)!=''#''
    insert @cols(fld,def,ord,sep,[type],[prec],scale,gtype)
    select
        c.name fld,
        dbo.fn__sql_def_typ(t.xusertype,t.name,c.length,c.prec,c.scale,c.isnullable) def,
        c.colorder,
        @sep,
        t.name,
        case when c.xtype=c.xusertype then cast(c.prec as sysname)  else null end,
        c.scale,
        case when t.xtype in (35,99,167,231,175,239) then ''S''
             when t.xtype in (34,165,36,173) then ''B''
             when t.xtype in (98) then ''V''
             else ''N''
        end
    from syscolumns c join systypes t on t.xusertype=c.xusertype
    where c.id=object_id(@tbl)
    and not c.name in (select fld from @ex)
else
    insert @cols(fld,def,ord,sep,[type],prec,scale,gtype)
    select
        c.name fld,
        dbo.fn__sql_def_typ(t.xusertype,t.name,c.length,c.prec,c.scale,c.isnullable) def,
        c.colorder,
        @sep,
        t.name,
        case when c.xtype=c.xusertype then cast(c.prec as sysname) else null end,
        c.scale,
        case when t.xtype in (35,99,167,231,175,239) then ''S''
             when t.xtype in (34,165,36,173) then ''B''
             when t.xtype in (98) then ''V''
             else ''N''
        end
    from tempdb..syscolumns c join systypes t on t.xusertype=c.xusertype
    where c.id=object_id(''tempdb..''+@tbl)
    and not c.name in (select fld from @ex)

update @cols set sep='''' where ord=(select max(ord) from @cols)
return
end -- fn__sql_def_cols'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__sql_def_cols:

-- =============================================================== fn__str_sheet
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_sheet',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131006
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_sheet') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_sheet') with nowait
        goto skip_fn__str_sheet
        end
    if @ver>131006
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_sheet') with nowait
        goto skip_fn__str_sheet
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_sheet') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_sheet'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_sheet]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,util_tkns
    v:131006\s.zaglio: optimized sep reduction
    v:130423\s.zaglio: a bug when @sep are spaces
    v:130201\s.zaglio: added str option
    v:121118\s.zaglio: preparing for group option
    v:120305\s.zaglio: now trim is default and addeded regular
    v:111114\s.zaglio: split a text into table by @sep
    t:
        select *
        from fn__str_sheet(''
            first name   last name   11/12/2011   232.43
                first name 1   last name 2    11/12/2011   33.2
            '',
            ''  '',
            default,default
        )
    t:
        select *
        from fn__str_sheet(''
            id  cod     des
            1   a       desc one
            2   b       desc two
            '',
            ''  '',
            default,''regular''
        )
    t:
        select *
        from fn__str_sheet(''
            id
            1
            2
            '',
            '' '',
            default,''regular''
        )
    */
CREATE function [dbo].[fn__str_sheet](
    @text nvarchar(max),
    @sep nvarchar(32),
    @sep_row nvarchar(4),
    @opt sysname)
returns @tbl table(
    id int identity,
    c00 sql_variant null,
    c01 sql_variant null,
    c02 sql_variant null,
    c03 sql_variant null,
    c04 sql_variant null,
    c05 sql_variant null,
    c06 sql_variant null,
    c07 sql_variant null,
    c08 sql_variant null,
    c09 sql_variant null,
    c10 sql_variant null
    )
as
begin
declare
    @c00 sql_variant,@c01 sql_variant,@c02 sql_variant,@c03 sql_variant,
    @c04 sql_variant,@c05 sql_variant,@c06 sql_variant,@c07 sql_variant,
    @c08 sql_variant,@c09 sql_variant,@c10 sql_variant,
    @c00o sql_variant,@c01o sql_variant,@c02o sql_variant,@c03o sql_variant,
    @c04o sql_variant,@c05o sql_variant,@c06o sql_variant,@c07o sql_variant,
    @c08o sql_variant,@c09o sql_variant
declare
    @p00 int,@p01 int,@p02 int,@p03 int,
    @p04 int,@p05 int,@p06 int,@p07 int,
    @p08 int,@p09 int,@p10 int
declare
    @st nvarchar(4000),@line nvarchar(4000),
    @i int,@j int,@pos int,@l int,
    @step int,@v sql_variant,
    @rpt char(1),           -- 1st char of separator that can repeat
    @lrpt int,              -- len of repeater (for future expansions)
    @dsc char(2),           -- double separator code
    @dsci char(2)           -- double separator code inverted

declare
    @notrim bit,            -- remove white space before and inside
    @regular bit,           -- columns are regularly columned
    @group bit,             -- group data when prev. cols as equals
    @str bit                -- do not convert numbers anda dates

if @text is null or @sep is null return

if @sep_row is null select @sep_row=crlf from fn__sym()

--initialize
select
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|''),
    @notrim=charindex(''|notrim|'',@opt),
    @regular=charindex(''|regular|'',@opt),
    @group=charindex(''|regular|'',@opt),
    @str=charindex(''|str|'',@opt)

if @notrim=0 select @dsc=char(17)+char(18),@dsci=char(18)+char(17)

select
    @p00=0,
    @st = '''',
    @rpt=left(@sep,1),
    @lrpt=1,
    @step = len(''.''+@sep+''.'')-2
if @sep = '''' and @step=0 select @sep = '' '',@step=1

select @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

declare cs cursor local for
    select
        case @notrim when 1 then token else ltrim(rtrim(token)) end
    from fn__str_table(@text,@sep_row)
    where ltrim(rtrim(token))!=''''
    order by pos
open cs
while 1=1
    begin
    fetch next from cs into @line
    if @@fetch_status!=0 break

    -- ============================================================== regular ==
    if @regular=1
        begin
        if charindex(@sep,@line)=0
            select @p00=1,@p01=len(@line)+1
        if @p00=0
            begin
            select @pos=0,@j=1,@l=len(@line)+1,@i=charindex(@sep,@line)
            if @i=0
                select @p00=1,@p01=@l
            else
                while @i!=0
                    begin
                    -- search next col

                    select @i=@i+@step,@pos=@pos+1
                    -- skip multiple separators like spaces
                    while @i<@l and substring(@line,@i,@lrpt)=@rpt select @i=@i+@lrpt
                    -- insert @tbl(c00,c01,c02,c03,c04)
                    -- select @j,@i,@pos,@regular,datalength(@sep)
                    if @pos=1  select @p00=@j,@p01=4000
                    if @pos=2  select @p01=@j,@p02=4000
                    if @pos=3  select @p02=@j,@p03=4000
                    if @pos=4  select @p03=@j,@p04=4000
                    if @pos=5  select @p04=@j,@p05=4000
                    if @pos=6  select @p05=@j,@p06=4000
                    if @pos=7  select @p06=@j,@p07=4000
                    if @pos=8  select @p07=@j,@p08=4000
                    if @pos=9  select @p08=@j,@p09=4000
                    if @pos=10 select @p09=@j,@p10=4000
                    select @j=@i,@i=charindex(@sep,@line,@j)
                    if @i=0 and @j<@l select @i=@l
                    end
                -- wend
            end -- header line

        select
            @c00=substring(@line,@p00,@p01-@p00),@c01=substring(@line,@p01,@p02-@p01),
            @c02=substring(@line,@p02,@p03-@p02),@c03=substring(@line,@p03,@p04-@p03),
            @c04=substring(@line,@p04,@p05-@p04),@c05=substring(@line,@p05,@p06-@p05),
            @c06=substring(@line,@p06,@p07-@p06),@c07=substring(@line,@p07,@p08-@p07),
            @c08=substring(@line,@p08,@p09-@p08),@c09=substring(@line,@p09,@p10-@p09),
            @c10=@c10

        end -- regular
    else
        begin
        if @notrim=0
            begin
            /*
            select @st=@sep+@sep
            while charindex(@st,@line)>0
                select @line=replace(@line,@st,@sep)
            */
            select @line=replace(
                            replace(
                                replace(@line,@sep,@dsc),@dsci,''''
                                ),@dsc,@sep
                            )
            end

        select @j=1,@pos=0,@line=@line,@l=len(@line)+1
        select @i = charindex(@sep, @line)
        if @i=0 select @i=@l
        select @c00=null,@c01=null,@c02=null,@c03=null,@c04=null,
               @c05=null,@c06=null,@c07=null,@c08=null,@c09=null

        while (@i <> 0)
            begin
            select @st=substring(@line, @j, @i - @j)
            if @notrim=0 select @st=ltrim(rtrim(@st))

            /*  this return an error
            select @v=
                case
                -- when isdate(@st)=1              then convert(datetime,@st)
                when dbo.fn__isnumeric(@st)=1   then convert(int,@st)
                else @st
                end
            */
            -- insert @tbl(c00,c01,c02,c03) select @pos,@i,@j,@st

            select @pos=@pos+1,
                   @j=@i+@step,
                   @i=charindex(@sep, @line,@j)
            if @i=0 and @j<@l select @i=@l

            if @str=1
                select @v=@st
            else
                begin
                if dbo.fn__isnumeric(@st)=1
                    select @v=convert(float,@st)
                else
                    select @v=@st
                end
            if @pos= 1 begin select @c00=@v continue end
            if @pos= 2 begin select @c01=@v continue end
            if @pos= 3 begin select @c02=@v continue end
            if @pos= 4 begin select @c03=@v continue end
            if @pos= 5 begin select @c04=@v continue end
            if @pos= 6 begin select @c05=@v continue end
            if @pos= 7 begin select @c06=@v continue end
            if @pos= 8 begin select @c07=@v continue end
            if @pos= 9 begin select @c08=@v continue end
            if @pos=10 begin select @c09=@v continue end

            end -- while cols

        end -- not regular

        -- store
        insert  @tbl select @c00,@c01,@c02,@c03,@c04,@c05,@c06,@c07,@c08,@c09,@c10
        select  @c00=@c00o,@c01=@c01o,@c02=@c02o,@c03=@c03o,@c04=@c04o,
                @c05=@c05o,@c06=@c06o,@c07=@c07o,@c08=@c08o,@c09=@c09o,@c10=@c10

    end -- while of cursor
close cs
deallocate cs

return
end -- fn__str_sheet'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_sheet:

-- =============================================================== fn__str_table
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_table',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130415
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_table') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_table') with nowait
        goto skip_fn__str_table
        end
    if @ver>130415
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_table') with nowait
        goto skip_fn__str_table
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_table') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_table'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_table]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    v:130415\s.zaglio: 4000->max
    v:120906\s.zaglio: replaced version with "with" because limited to 100 recursions
    v:111114\s.zaglio: added to grp util_tkns and commented inside a 20% faster method mssql2k5>
    v:101201\s.zaglio: added pkey index on pos
    v:100916\s.zaglio: added cross apply example
    v:100314\s.zaglio: managed null @data
    v:090812\s.zaglio: a bug with double sep
    v:081130\s.zaglio: verticalizee a splitted variable
    t:select * from dbo.fn__str_table(''a|b|c'',''|'')
    t:select * from dbo.fn__str_table(''a\n\nc'',''\n'') <-- is correct have only one line empty
    t:select * from dbo.fn__str_table(null,'';'')
    t:select * from dbo.fn__str_table(''a b c d'','''')
    t:
        select l,s.* from (
            select ''1|a|aa'' as l union
            select ''2|b|bb'' as l union
            select ''3|c|cc'' as l union
            select ''4|d|dd'' as l
        ) as data
        cross apply dbo.fn__str_table(data.l,''|'') s
*/
create function [dbo].[fn__str_table](
  @data nvarchar (max),
  @sep nvarchar (32)=''|''
  )
returns
    @t table (pos int identity(1,1) primary key, token nvarchar(4000))
begin
declare @st nvarchar(max)
declare @pos int,@opos int
declare @step int

if @data is null or @sep is null return

--initialize
select @st = ''''
select @step = len(''.''+@sep+''.'')-2
if @sep = '''' and @step=0 select @sep = '' '',@step=1

select @data = @data + @sep , @opos=1
select @pos = charindex(@sep, @data)

while (@pos <> 0)
    begin
    set @st = substring(@data, @opos, @pos - @opos)
    insert into @t (token) values (@st)
    set @opos=@pos+@step
    set @pos = charindex(@sep, @data,@opos)
    end

return
end -- fn__str_table'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_table:

-- =========================================================== fn__str_unpattern
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_unpattern',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120528
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_unpattern') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_unpattern') with nowait
        goto skip_fn__str_unpattern
        end
    if @ver>120528
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_unpattern') with nowait
        goto skip_fn__str_unpattern
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_unpattern') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_unpattern'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_unpattern]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    g:utility
    v:120528\s.zaglio: extract only what change
    t:
        select * from fn__str_unpattern(''
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 101
        error(-811561914) "local object "FN_GET_2OF5_CHECKDIGIT" is different" in "sp__script_alias"
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 102
        error(-811561914) "local object "FN_GET_3OF9_CHECKDIGIT" is different" in "sp__script_alias"
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 103
        error(-811561914) "local object "FN_GET_ID_ENTITY_FROM_CD_ERP_SHIPMENT" is different" in "sp__script_alias"
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 103
        error(-811561914) "local object "SP_MANAGE_MAIL_QUEUE" is different" in "sp__script_alias"
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 103
        error(-811561914) "local object "SP_REFRESH_VIEWS" is different" in "sp__script_alias"
        '',''[0-9]%'',''unlike'') -- excludes 101 and 102

*/
create function fn__str_unpattern(
    @blob ntext,            -- text
    @filter sysname,        -- value for optional condition
    @opt sysname            -- conditions
    )
returns @t table(lno int,line nvarchar(4000))
as
begin

declare
    @drop bit,
    @i int,@j int,@l int,@n int,@p int,
    @ncrlf nvarchar(2),@cr nchar(1),@lf nchar(1),
    @lcrlf int,@dbg bit,
    @line nvarchar(4000),@lno int,
    @diff int

select @dbg=0

declare @words table (word sysname)
declare @src table (lno int identity primary key,line nvarchar(4000))

select
    @ncrlf=crlf,@cr=cr,@lf=lf,
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
from fn__sym()

insert @src select ltrim(rtrim(line)) from fn__ntext_to_lines(@blob,0)

-- delete all identic lines
delete from @src
where line in (
    select line from @src group by line having count(*)>1
    )

-- isolate duplicated words
insert @words(word)
select token
from @src src
cross apply fn__str_table(line,'''') b
group by token
having count(*)>1

-- remove duplicated from lines
declare cs cursor local for
    select lno,line
    from @src
open cs
while 1=1
    begin
    fetch next from cs into @lno,@line
    if @@fetch_status!=0 break
    update @words set @line=replace(@line,word,'''') from @words
    select @line=ltrim(rtrim(@line))
    if isnull(@line,'''')='''' continue
    while left(@line,1) in ('''''''',''"'',''['',''('',''{'') and
          right(@line,1) in ('''''''',''"'','']'','')'',''}'')
        select @line=substring(@line,2,len(@line)-2)
    insert @t(lno,line) select @lno,@line
    end -- while of cursor
close cs
deallocate cs

if @opt!=''||''
    begin
    if charindex(''|unlike|'',@opt)>0 and not @filter is null
        delete from @t where line like @filter
    if charindex(''|like|'',@opt)>0 and not @filter is null
        delete from @t where not line like @filter
    end

return
end -- fn__str_unpattern'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_unpattern:

-- =============================================================== fn__str_words
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__str_words',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130509
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__str_words') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__str_words') with nowait
        goto skip_fn__str_words
        end
    if @ver>130509
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__str_words') with nowait
        goto skip_fn__str_words
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__str_words') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__str_words'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__str_words]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,util_tkns
    v:130509\s.zaglio: a small bug on last word
    v:130505\s.zaglio: added min option
    v:120403\s.zaglio: remake of csv parse using (faster) code
    v:120402\s.zaglio: refine of csv option using (slow) regexp
    v:120329\s.zaglio: added more columns and csv option
    v:111114\s.zaglio: split a line into words by @sep
    t:
        select * from fn__str_words('' first name   last name   age  '','' '',default)
        select * from fn__str_words('' first name   last name   age  '',''  '',default)
        select * from fn__str_words(''   first name     last name   age  '',''  '',default)
        select * from fn__str_words(''   first name     last name   age  '',''  '',''min'')
    t:
        select * from fn__str_words(''a,b,c,"d""",e,"",".,.",f'','','',default)
        select * from fn__str_words(''a,b,c,"d""",e,"",".,.,.",f,",",g'','','',''csv'')
*/
CREATE function [dbo].[fn__str_words](
    @line nvarchar(4000),
    @sep nvarchar(32),
    @opt sysname)
returns @tbl table(
    c00 nvarchar(4000) null,
    c01 nvarchar(4000) null,
    c02 nvarchar(4000) null,
    c03 nvarchar(4000) null,
    c04 nvarchar(4000) null,
    c05 nvarchar(4000) null,
    c06 nvarchar(4000) null,
    c07 nvarchar(4000) null,
    c08 nvarchar(4000) null,
    c09 nvarchar(4000) null,
    c10 nvarchar(4000) null,
    c11 nvarchar(4000) null,
    c12 nvarchar(4000) null,
    c13 nvarchar(4000) null,
    c14 nvarchar(4000) null,
    c15 nvarchar(4000) null,
    c16 nvarchar(4000) null,
    c17 nvarchar(4000) null,
    c18 nvarchar(4000) null,
    c19 nvarchar(4000) null,
    c20 nvarchar(4000) null,
    c21 nvarchar(4000) null,
    c22 nvarchar(4000) null,
    c23 nvarchar(4000) null,
    c24 nvarchar(4000) null,
    c25 nvarchar(4000) null,
    c26 nvarchar(4000) null,
    c27 nvarchar(4000) null,
    c28 nvarchar(4000) null,
    c29 nvarchar(4000) null,
    c30 nvarchar(4000) null,
    c31 nvarchar(4000) null
    )
as
begin
/*
    declare @n int select @n=32
    -- generate table cols
    select ''    c''+row+'' nvarchar(4000) null,''
    from (
        select right(''00''+cast(row as varchar),2) row
        from fn__range(0,@n-1,1)
        ) a

    -- generate vars
    select ''    @c''+row+'' nvarchar(4000),''
    from (
        select right(''00''+cast(row as varchar),2) row
        from fn__range(0,@n-1,1)
        ) a

    -- generate if
    select ''    ''+
        ''if @pos=''+row+'' begin ''+
        ''select @c''+right(''00''+row,2)+''=@v continue end''
    from (
        select right(''00''+cast(row as varchar),2) row
        from fn__range(0,@n-1,1)
        ) a

    -- generate col of select
    select ''    @c''+row+'',''
    from (
        select right(''00''+cast(row as varchar),2) row
        from fn__range(0,@n-1,1)
        ) a

    -- generate col of select for rxp
    select ''    select @c''+row+''=token from @rr where id=''+row
    from (
        select right(''00''+cast(row as varchar),2) row
        from fn__range(0,@n-1,1)
        ) a

*/

declare
    @c00 nvarchar(4000),
    @c01 nvarchar(4000),
    @c02 nvarchar(4000),
    @c03 nvarchar(4000),
    @c04 nvarchar(4000),
    @c05 nvarchar(4000),
    @c06 nvarchar(4000),
    @c07 nvarchar(4000),
    @c08 nvarchar(4000),
    @c09 nvarchar(4000),
    @c10 nvarchar(4000),
    @c11 nvarchar(4000),
    @c12 nvarchar(4000),
    @c13 nvarchar(4000),
    @c14 nvarchar(4000),
    @c15 nvarchar(4000),
    @c16 nvarchar(4000),
    @c17 nvarchar(4000),
    @c18 nvarchar(4000),
    @c19 nvarchar(4000),
    @c20 nvarchar(4000),
    @c21 nvarchar(4000),
    @c22 nvarchar(4000),
    @c23 nvarchar(4000),
    @c24 nvarchar(4000),
    @c25 nvarchar(4000),
    @c26 nvarchar(4000),
    @c27 nvarchar(4000),
    @c28 nvarchar(4000),
    @c29 nvarchar(4000),
    @c30 nvarchar(4000),
    @c31 nvarchar(4000)

declare
    @st nvarchar(4000),
    @i int,@j int,@pos int,@l int,
    @step int,@v nvarchar(4000),
    @tmp nvarchar(4000),            -- temporary @v for csv use
    @csv int,                       -- enabled csv management
    @wd nchar(1),                   -- csv word delimiter
    @minimize bit,                  -- reduce multi-sep to one
    @dbg bit,                       -- enable returns of dbg info
    @dsep nvarchar(64),
    @end_declare bit

if @line is null or @sep is null return

-- options
if not @opt is null
    begin
    select @opt=''|''+@opt+''|''
    -- comma separated values
    select @csv=charindex(''|csv'',@opt)
    select @minimize=charindex(''|min|'',@opt)
    select @dbg=charindex(''|dbg|'',@opt)
    if @csv>0 select @wd=''"'',@csv=1
    end
else
    select @minimize=0,@csv=0

--initialize
select @st = ''''
select @step = len(''.''+@sep+''.'')-2
if @sep = '''' and @step=0 select @sep = '' '',@step=1

/*
if @minimize=1
    begin
    select @dsep=@sep+@sep
    while charindex(@dsep,@line)>0 select @line=replace(@line,@dsep,@sep)
    -- return
    end
*/

-- if right(@line,@step)!=@sep select @line = @line + @sep
select @j=1,@pos=-1
select @i = charindex(@sep, @line),@l=len(@line+''.'')

if @dbg=1
    insert @tbl(c00,c01,c02,c03,c04,c05,c06)
    select ''@line'',''@i'',''@j'',''@step'',''@sep'',''@v'',''@l''


while (@j<@l)
    begin

    select @v=substring(@line, @j, @i - @j)

    if @dbg=1
        insert @tbl(c00,c01,c02,c03,c04,c05,c06)
        select @line, @i,@j,@step,@sep,@v,@l

    -- skip multi-separators
    if @minimize=1
        while @i<@l and substring(@line,@i+@step,@step)=@sep
            select @i=@i+@step

    select @j=@i+@step

    /*
        select * from fn__str_words(
            ''   first name     last name   age  4'',''  '',
           --1234567890123456789012345678901234567890
           --  0          1   1         2 3   3
            ''min|dbg''
        )
        select * from fn__str_words(
            ''   first name     last name   age  4'',''  '',
            ''dbg''
        )
    */

    select @i = charindex(@sep, @line,@j)
    if @i=0 select @i=@l

    if @csv=0
        select @pos=@pos+1
    else
        begin

        if @v=@wd
            begin
            if @tmp is null
                select @tmp=@wd+@sep,@v=null
            else
                select @v=@tmp+@wd,@tmp=null
            end

        if not @v is null
            begin
            if left(@v,1)=@wd and right(@v,1)=@wd
                select @pos=@pos+1,@v=replace(substring(@v,2,len(@v)-2),@wd+@wd,@wd)
            else
                begin
                if left(@v,1)!=@wd and @tmp is null
                    select @pos=@pos+1
                else
                    begin
                    if right(@v,1)=@wd
                        select
                            @v=replace(
                                    substring(@tmp+@v,2,len(@tmp)+len(@v)-2),
                                    @wd+@wd,@wd
                                    ),
                            @pos=@pos+1,
                            @tmp=null
                    else
                        select @tmp=isnull(@tmp,'''')+@v+@sep,@v=null
                    end
                end -- half value
            end -- if @v
        end -- csv

    if not @v is null
        begin
        if @pos=00 begin select @c00=@v continue end
        if @pos=01 begin select @c01=@v continue end
        if @pos=02 begin select @c02=@v continue end
        if @pos=03 begin select @c03=@v continue end
        if @pos=04 begin select @c04=@v continue end
        if @pos=05 begin select @c05=@v continue end
        if @pos=06 begin select @c06=@v continue end
        if @pos=07 begin select @c07=@v continue end
        if @pos=08 begin select @c08=@v continue end
        if @pos=09 begin select @c09=@v continue end
        if @pos=10 begin select @c10=@v continue end
        if @pos=11 begin select @c11=@v continue end
        if @pos=12 begin select @c12=@v continue end
        if @pos=13 begin select @c13=@v continue end
        if @pos=14 begin select @c14=@v continue end
        if @pos=15 begin select @c15=@v continue end
        if @pos=16 begin select @c16=@v continue end
        if @pos=17 begin select @c17=@v continue end
        if @pos=18 begin select @c18=@v continue end
        if @pos=19 begin select @c19=@v continue end
        if @pos=20 begin select @c20=@v continue end
        if @pos=21 begin select @c21=@v continue end
        if @pos=22 begin select @c22=@v continue end
        if @pos=23 begin select @c23=@v continue end
        if @pos=24 begin select @c24=@v continue end
        if @pos=25 begin select @c25=@v continue end
        if @pos=26 begin select @c26=@v continue end
        if @pos=27 begin select @c27=@v continue end
        if @pos=28 begin select @c28=@v continue end
        if @pos=29 begin select @c29=@v continue end
        if @pos=30 begin select @c30=@v continue end
        if @pos=31 begin select @c31=@v continue end
        end

    end -- word loop

insert @tbl
select
    @c00,
    @c01,
    @c02,
    @c03,
    @c04,
    @c05,
    @c06,
    @c07,
    @c08,
    @c09,
    @c10,
    @c11,
    @c12,
    @c13,
    @c14,
    @c15,
    @c16,
    @c17,
    @c18,
    @c19,
    @c20,
    @c21,
    @c22,
    @c23,
    @c24,
    @c25,
    @c26,
    @c27,
    @c28,
    @c29,
    @c30,
    @c31
return
end -- fn__str_words'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__str_words:

-- ============================================================ fn__trace_events
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__trace_events',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131006
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__trace_events') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__trace_events') with nowait
        goto skip_fn__trace_events
        end
    if @ver>131006
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__trace_events') with nowait
        goto skip_fn__trace_events
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__trace_events') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__trace_events'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__trace_events]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131006\s.zaglio:svr->srv
    v:120213\s.zaglio:back to cod/des notation
    v:110312\s.zaglio:list sysobjects types
    t:select * from fn__trace_events(default,default,default)
    t:sp__find ''events''
*/
CREATE function fn__trace_events(@p1 bit=null,@p2 bit=null,@p3 bit=null)
returns @events table (
    id int,rid int,flags smallint null,
    cod sysname
    )
as
begin
-- see entire list in
-- ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.it/udb9/html/fb2a7bd0-2347-488c-bb75-734098050c7c.htm
declare @svr smallint,@db smallint
select top 1 @svr=srv|ddl,@db=db|ddl from flags
insert @events(id,      rid,    flags,  cod)
        select 010,      null,   @svr,   ''DDL_SERVER_LEVEL_EVENTS''
union   select 011,      010,    @svr,   ''create_database''
union   select 012,      010,    @svr,   ''alter_database''
union   select 013,      010,    @svr,   ''drop_database''
union   select 070,      010,    @db,    ''DDL_DATABASE_LEVEL_EVENTS''

union   select 080,      070,    @db,    ''DDL_TABLE_VIEW_EVENTS''

union   select 090,      080,    @db,    ''DDL_TABLE_EVENTS''
union   select 091,      090,    @db,    ''create_table''
union   select 092,      090,    @db,    ''alter_table''
union   select 093,      090,    @db,    ''drop_table''

union   select 100,      080,    @db,    ''DDL_TABLE_EVENTS''
union   select 101,      100,    @db,    ''create_view''
union   select 102,      100,    @db,    ''alter_view''
union   select 103,      100,    @db,    ''drop_view''

union   select 110,      080,    @db,    ''DDL_INDEX_EVENTS''
union   select 111,      110,    @db,    ''create_index''
union   select 112,      110,    @db,    ''alter_index''
union   select 113,      110,    @db,    ''drop_index''
union   select 114,      110,    @db,    ''create_xml_index''

union   select 130,      070,    @db,    ''DDL_SYNONYM_EVENTS''
union   select 131,      130,    @db,    ''create_synonym''
union   select 132,      130,    @db,    ''drop_synonym''

union   select 140,      070,    @db,    ''DDL_FUNCTION_EVENTS''
union   select 141,      140,    @db,    ''create_function''
union   select 142,      140,    @db,    ''alter_function''
union   select 143,      140,    @db,    ''drop_function''

union   select 150,      070,    @db,    ''DDL_PROCEDURE_EVENTS''
union   select 151,      150,    @db,    ''create_procedure''
union   select 152,      150,    @db,    ''alter_procedure''
union   select 153,      150,    @db,    ''drop_procedure''

union   select 160,      070,    @db,    ''DDL_TRIGGER_EVENTS''
union   select 161,      160,    @db,    ''create_trigger''
union   select 162,      160,    @db,    ''alter_trigger''
union   select 163,      160,    @db,    ''drop_trigger''

union   select 170,      070,    @db,    ''DDL_TYPE_EVENTS''
union   select 171,      170,    @db,    ''create_type''
union   select 173,      170,    @db,    ''drop_type''

union   select 200,      070,    @db,    ''DDL_DATABASE_SECURITY_EVENTS''

union   select 220,      200,    @db,    ''DDL_USER_EVENTS''
union   select 221,      220,    @db,    ''create_user''
union   select 222,      220,    @db,    ''alter_user''
union   select 223,      220,    @db,    ''drop_user''

union   select 230,      200,    @db,    ''DDL_ROLE_EVENTS''
union   select 231,      230,    @db,    ''create_role''
union   select 232,      230,    @db,    ''alter_role''
union   select 233,      230,    @db,    ''drop_role''

union   select 250,      200,    @db,    ''DDL_SCHEMA_EVENTS''
union   select 251,      250,    @db,    ''create_schema''
union   select 252,      250,    @db,    ''alter_schema''
union   select 253,      250,    @db,    ''drop_schema''

union   select 260,      200,    @db,    ''DDL_GDR_DATABASE_EVENTS''
union   select 261,      260,    @db,    ''grant_database''
union   select 262,      260,    @db,    ''deny_database''
union   select 263,      260,    @db,    ''revoke_database''

return
end -- fn__trace_events'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__trace_events:

-- =================================================================== fn__xtype
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='fn__xtype',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110312
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'fn__xtype') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'fn__xtype') with nowait
        goto skip_fn__xtype
        end
    if @ver>110312
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'fn__xtype') with nowait
        goto skip_fn__xtype
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'fn__xtype') with nowait

if exists(
    select top 1 null from sys.objects
    where name='fn__xtype'

    and schema_id=schema_id('dbo')
    )
    drop function [dbo].[fn__xtype]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110312\s.zaglio:list sysobjects types
    t:select * from fn__xtype()
    t:
        select *
        from (
            select distinct xtype from sysobjects
            ) t
        left join fn__xtype() f
        on t.xtype=f.xtype
    t:select * from sysobjects where xtype in (''it'',''sq'')
*/
CREATE function fn__xtype()
returns @xtype table ([xtype] nvarchar(2),[name] sysname,sqlver int null)
as
begin
insert @xtype
        select ''C'', ''vincolo CHECK'',null
union   select ''D'', ''valore predefinito o vincolo DEFAULT'',null
union   select ''F'', ''vincolo FOREIGN KEY'',null
union   select ''L'', ''log'',null
union   select ''FN'',''funzione scalare'',null
union   select ''IF'',''funzione inline valutata a livello di tabella'',null
union   select ''P'', ''stored procedure'',null
union   select ''PK'',''vincolo PRIMARY KEY (tipo K)'',null
union   select ''RF'',''stored procedure del filtro di replica'',null
union   select ''S'', ''tabella di sistema'',null
union   select ''TF'',''funzione di tabella'',null
union   select ''TR'',''trigger'',null
union   select ''U'', ''tabella utente'',null
union   select ''UQ'',''vincolo UNIQUE (tipo K)'',null
union   select ''V'', ''vista'',null
union   select ''X'', ''stored procedure estesa'',null
union   select ''SN'',''sininimo'',2005
union   select ''AF'',''funzione di aggregazione (CLR)'',2005
union   select ''PC'',''stored procedure assembly (CLR)'',2005
union   select ''FS'',''funzione scalare assembly (CLR)'',2005
union   select ''FT'',''funzione valutata a livello di tabella assembly (CLR)'',2005
union   select ''R'', ''regola (tipo obsoleto, autonoma)'',2005
union   select ''SQ'',''coda di servizio'',2005
union   select ''TA'',''trigger DML assembly (CLR)'',2005
union   select ''TR'',''trigger DML SQL'',2005
union   select ''UQ'',''vincolo UNIQUE'',2005
union   select ''IT'',''tabella interna'',2005

return
end -- fn__xtype'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_fn__xtype:

-- ======================================================================= enums
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='enums',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130217
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'enums') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'enums') with nowait
        goto skip_enums
        end
    if @ver>130217
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'enums') with nowait
        goto skip_enums
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'enums') with nowait

if exists(
    select top 1 null from sys.objects
    where name='enums'

    and schema_id=schema_id('dbo')
    )
    drop view [dbo].[enums]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this for other app
    l:see LICENSE file
    g:obj,utility
    v:130217\s.zaglio: generic enumerators
*/
CREATE view enums
as
select
    -- fn__str_between
  null as [btw.close_left],     -- search @from and then the next @to
     1 as [btw.close_right],    -- search @to and then the previous @from

    -- end
     -1 as [last]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_enums:

-- ================================================================== sp__backup
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__backup',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120726
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__backup') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__backup') with nowait
        goto skip_sp__backup
        end
    if @ver>120726
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__backup') with nowait
        goto skip_sp__backup
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__backup') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__backup'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__backup]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120726\s.zaglio: print more info
    v:110324\s.zaglio: modern version
    v:081130\S.Zaglio: rewrited version for general porpouse backup. See also sp__restore
    t:declare @r int exec @r=sp__backup ''%temp%'',@dbg=1,@opt=''doit'' exec sp__printf ''result:%s'',@r
    t:xp__cmdshell ''del "%temp%\*.bak"''
*/
CREATE proc [dbo].[sp__backup]
    @device     nvarchar(1024)=null out,
    @db         sysname=null,
    @opt        sysname=null,
    @dbg        int=0
as
begin
set nocount on

declare @proc sysname,@ret int
select  @proc=object_name(@@procid),@ret=0,
        @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @device is null goto help

declare @tmp nvarchar(1024),@dt sysname,@sql nvarchar(4000)

if @db is null select @db=db_name()
select @dt=convert(nvarchar(16),getdate(),12)+''_''+left(replace(convert(nvarchar(16),getdate(),108),'':'',''''),4)

exec sp__get_temp_dir @tmp out
select @tmp=@tmp+''\''+@db+''_''+@dt+''.bak''

if @dbg=1 exec sp__printf ''dev=%s, tmp=%s, db=%s, dt=%s'',@device, @tmp,@db,@dt

-- return the temp file name
if @device=''%temp%'' select @device=@tmp

select @device=replace(@device,''%db%'',@db)
select @device=replace(@device,''%t%'',@dt)

if @dbg=1 exec sp__printf ''dev=%s, tmp=%s, db=%s, dt=%s'',@device, @tmp,@db,@dt

-- if not dest path specified
if left(@device,2)=''\\'' goto err_net

/*
for net feature
if not @move_to is null begin

    declare @src nvarchar(512)
    declare @cmd nvarchar(1024)
    set @src=@dst
    set @bak=dbo.fn__str_at(@move_to,''|'',1)
    set @uid=coalesce(dbo.fn__str_at(@move_to,''|'',2),'''')
    set @pwd=coalesce(dbo.fn__str_at(@move_to,''|'',3),'''')

    if @uid<>'''' or @pwd<>'''' begin
        set @i=charindex(''\\'',@bak)
        if @i<>0 set @i=@i+2
        set @i=charindex(''\'',@bak,@i)
        set @cmd=''net use ''+substring(@bak,1,@i-1)+'' ''+@pwd+'' /user:''+@uid
        if @dbg=1 and @simul=0 print @cmd
        if @simul=1 print @cmd
        if @simul=0 exec sp__run_cmd @cmd,@tmp_table=@tmp_table out,@nodrop=1,@nooutput=@out
    end
    if @simul=0 begin            -- insert backup command
        set @cmd=''insert into ''+@tmp_table+'' values (''''''+dbo.fn__inject(@sql)+'''''')''
        exec(@cmd)
    end
-- t: sp__backup @device=''\\gamon\Backup\%db_name%.bak|seldom\stefano|prova'',@simul=0
    set @cmd=''copy "''+@src+''" "''+@bak+''"''
    if @dbg=1 and @simul=0 print @cmd
    if @simul=0 exec sp__run_cmd @cmd,@tmp_table=@tmp_table out,@nodrop=1,@nooutput=@out
    else print @cmd
    if @uid<>''''  or @pwd<>'''' begin
        set @i=charindex(''\\'',@bak)
        if @i<>0 set @i=@i+2
        set @i=charindex(''\'',@bak,@i)
        set @cmd=''net use ''+substring(@bak,1,@i-1)+'' /delete''
        if @dbg=1 and @simul=0 print @cmd
        if @simul=0 exec sp__run_cmd @cmd,@tmp_table=@tmp_table,@nodrop=1,@nooutput=@out
        else print @cmd
    end

    -- delete local backup
    set @cmd=''del /q "''+@src+''"''
    if @dbg=1 and @simul=0 print @cmd
    if @simul=0 exec sp__run_cmd @cmd,@tmp_table=@tmp_table out,@nodrop=1,@nooutput=@out
    else print @sql
    end
*/
select @sql =''BACKUP DATABASE [''+isnull(@db,''?db?'')+'']''
            +'' TO  DISK = '''''' + isnull(@device,''?device?'') + ''''''''
            +'' WITH NAME = N''''FROM SP__BACKUP'''',  STATS = 10''

if charindex(''|diff|'',@opt)>0
    select @sql=@sql+'',NOSKIP,NOINIT,NOUNLOAD,DIFFERENTIAL''
else
    select @sql=@sql+'',INIT,FORMAT''

if charindex(''|doit|'',@opt)>0
or charindex(''|run|'',@opt)>0
    begin
    exec(@sql)
    if @@error!=0 select @ret=-2
    else
        exec sp__printf ''-- sp__restore new_db,''''%s'''',@opt=''''run'''''',@device
    end
else
    exec sp__printf ''%s'',@sql

dispose:
goto ret

-- =================================================================== errors ==
err_net:    exec @ret=sp__err ''not yet implemented'',@proc goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    backup a db to a file that can be local, a temp dir or a network path

Parameters
    @device     file or net path or %temp% if want a fast backup into tmp dir
                if start with \\ can add usr and pwd at end, separated by |
                can contain %db%, %t% that will be replaced with db name and time stamp
    @db         if not current db
    @opt        options
                run     modern version of old "doit"
                doit    execute it effectivelly instead of print instruction
                diff    do a differential backup
''
select @ret=-1

ret:
return @ret
end -- sp__backup'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__backup:

-- =========================================================== sp__backup_server
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__backup_server',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110726
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__backup_server') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__backup_server') with nowait
        goto skip_sp__backup_server
        end
    if @ver>110726
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__backup_server') with nowait
        goto skip_sp__backup_server
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__backup_server') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__backup_server'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__backup_server]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110726\s.zaglio:modified default name from %db%_%t in %db%
    v:110720\s.zaglio:added @include
    v:110719\s.zaglio:sp__backup_server
    t:sp__backup_server ''c:\backupSQL'',@exclude=''ReportServer$WEBAPP'',@dbg=1
    t:xp_cmdshell ''dir c:\backupSQL''
*/
CREATE proc sp__backup_server
    @path       nvarchar(512) = null,
    @include    nvarchar(4000)= null,
    @exclude    nvarchar(4000)= null,
    @opt        sysname = null,
    @dbg        int = 0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare @db sysname,@device nvarchar(1024),@psep nvarchar(32)
create table #exclude ([name] sysname)
create table #include ([name] sysname)

-- =========================================================== initialization ==
select @psep=psep from fn__sym()

insert #exclude([name])
select ''%tempdb%'' union
select ''master'' union
select ''msdb'' union
select ''model'' union
select token from dbo.fn__str_table(@exclude,''|'')

if @include is null
    insert #include([name]) select ''%''
else
    insert #include([name])
    select token from dbo.fn__str_table(@include,''|'')

select db.[name]
into #dbs
-- select db.[name]
from master..sysdatabases db
join #include inc on db.[name] like inc.[name]
left join #exclude ex on db.[name] like ex.[name]
where [status] & (512|4096) = 0
and ex.[name] is null

drop table #exclude
/*
1 = autoclose (ALTER DATABASE)
4 = select into/bulkcopy (ALTER DATABASE tramite SET RECOVERY)
8 = trunc. log on chkpt (ALTER DATABASE tramite SET RECOVERY)
16 = torn page detection (ALTER DATABASE)
32 = loading
64 = pre recovery
128 = recovering
256 = not recovered
512 = offline (ALTER DATABASE)
1024 = read only (ALTER DATABASE)
2048 = dbo use only (ALTER DATABASE tramite SET RESTRICTED_USER)
4096 = single user (ALTER DATABASE)
32768 = emergency mode
4194304 = autoshrink (ALTER DATABASE)
1073741824 = cleanly shutdown
*/

if right(@path,1)!=@psep select @path=@path+@psep

-- ======================================================== second params chk ==
if @path is null goto help

-- ===================================================================== body ==

declare cs cursor local for
    select [name]
    from #dbs
    where 1=1
open cs
while 1=1
    begin
    fetch next from cs into @db
    if @@fetch_status!=0 break

    if charindex(''%db%'',@path)=0 select @device=@path+''%db%''
    if charindex(''.'',@path)=0 select @device=@device+''.bak''

    if @dbg=0
        begin
        exec @ret=sp__backup @device out,@db,@opt=''doit''
        if @ret=0 exec sp__printf ''### %s backed up to "%s" ###'',@db,@device
        end
    else
        exec @ret=sp__backup @device out,@db
    end -- while of cursor
close cs
deallocate cs

goto ret

-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    backup all online and non single user db of this server

Parameters
    @path       is the destination folder
    @include    list of db to include, separated by |. Can use %.
    @exclude    list of db to exclude, separated by |. Can use %.
                (%tempdb% are always excluded)
    @opt        options
                not used
    @dbg        1, show simulation

-- List of DB that will be saved --
''

exec sp__select_astext ''select * from #dbs order by 1''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__backup_server'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__backup_server:

-- ================================================================== sp__checks
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__checks',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080716
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__checks') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__checks') with nowait
        goto skip_sp__checks
        end
    if @ver>080716
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__checks') with nowait
        goto skip_sp__checks
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__checks') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__checks'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__checks]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080716\S.Zaglio: disable checks for all tables of optional other db
*/
CREATE PROCEDURE sp__checks
    @action nchar(2)=''-?'',
    @db_dst nvarchar(128)=null
AS
begin
declare @dbg bit
set @dbg=1
if @db_dst is null set @db_dst=db_name()

declare @sql nvarchar(512)
if @action=''-?'' goto usage
if @action=''EA'' -- enable all
    begin
    if @dbg=1 print ''enable all constraint''
    set @sql=''use ''+@db_dst+'' alter table ? CHECK CONSTRAINT ALL ''
    if @dbg=1 exec sp__inject @sql out,''print '''''',''''''''
    exec sp__foreachobj ''TABLE'',@sql
    end
if @action=''DA'' -- disable all
    begin
    if @dbg=1 print ''disable all constraint''
    set @sql=''use ''+@db_dst+'' alter table ? NOCHECK CONSTRAINT ALL ''
    if @dbg=1 exec sp__inject @sql out,''print '''''',''''''''
    exec sp__foreachobj ''TABLE'',@sql
    end
return
usage:
    print ''sp__checks''
    print ''    @action nchar(2)=''''-?''''''
    print ''    @tbl nvarchar(128)=''''%''''''
    print ''    @db_dst nvarchar(128)=null''
    print ''usage:''
    print '' sp_t -? this help''
    print '' sp_t EA enable all triggers''
    print '' sp_t DA disable all triggers''
    return
test:
    exec sp__triggers ''DA'',@tbl=''a%''
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__checks:

-- ================================================================ sp__chknulls
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__chknulls',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110210
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__chknulls') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__chknulls') with nowait
        goto skip_sp__chknulls
        end
    if @ver>110210
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__chknulls') with nowait
        goto skip_sp__chknulls
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__chknulls') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__chknulls'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__chknulls]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110210\s.zaglio: added more params
    v:101103\s.zaglio: more explicit sample
    v:080817\S.Zaglio: check if all parameters are nulls (use in params check)
    t:
        declare @r int
        exec @r=sp__chknulls ''a|b|c'',1,null,''a'',@dbg=1
        print @r    -->0
        exec @r=sp__chknulls null,null,null,null,@dbg=1
        print @r    -->1
*/
CREATE proc [dbo].[sp__chknulls]
    @p0 sql_variant=null,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null,
    @p5 sql_variant=null,
    @p6 sql_variant=null,
    @p7 sql_variant=null,
    @p8 sql_variant=null,
    @p9 sql_variant=null,
    @dbg bit=0
as
begin
declare @r int
if  @p0 is null and
    @p1 is null and @p2 is null and @p3 is null and
    @p4 is null and @p5 is null and @p6 is null and
    @p7 is null and @p8 is null and @p9 is null
    begin
    set @r=1
    if @dbg=1 print ''all nulls''
    end
else set @r=0
return @r
end -- [sp__chknulls]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__chknulls:

-- ================================================================== sp__circle
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__circle',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120924
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__circle') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__circle') with nowait
        goto skip_sp__circle
        end
    if @ver>120924
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__circle') with nowait
        goto skip_sp__circle
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__circle') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__circle'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__circle]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,draw
    k:draw,circle,text,buffer
    v:120924\s.zaglio: draw a circle into a text buffer
    t:sp__circle 77
*/
CREATE proc sp__circle
    @r int = null,
    @x int = null,
    @y int = null,
    @buffer nvarchar(max) = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @opt1 bit,@opt2 bit,
    @i int,@j int,
    @crlf varchar(2),
    @diff int,
    @out bit,
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @opt1=charindex(''|opt|'',@opt),
    @out=case when @buffer is null then 1 else 0 end,
    @buffer=isnull(@buffer,''''),
    @crlf=crlf,
    @end_declare=1
from fn__sym()

-- ======================================================== second params chk ==
if @r is null -- @opt=''||''
    goto help

-- ===================================================================== body ==

select @i=-@r
while @i<=@r
    begin
    select @j=-@r
    while @j<=@r
        begin
        select @diff=round(sqrt(@i*@i+@j*@j)-@r,0)
        select @buffer=@buffer+case @diff when 0 then ''*'' else '' '' end+'' ''
        select @j=@j+1
        end -- j
    select @buffer=@buffer+@crlf,@i=@i+1
    end -- i

if @out=1 exec sp__printsql @buffer

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
/*
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_me1:    select @e_msg=''write here msg''                              goto err
err_me2:    select @e_msg=''write this %s'',@e_p1=@var                    goto err
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    draw a circle into buffer or video

Parameters
    @r      radius
    @x,@y   center (TODO)
    @buffer predefined @buffer
    @opt    options (not used)

Examples
    exec sp__circle 7
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__circle'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__circle:

-- ================================================================= sp__comment
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__comment',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100328.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__comment') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__comment') with nowait
        goto skip_sp__comment
        end
    if @ver>100328.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__comment') with nowait
        goto skip_sp__comment
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__comment') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__comment'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__comment]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100328.1000\s.zaglio: support of proc,func,view...
    v:100321\s.zaglio: support for quoted table name
    v:100228\s.zaglio: added more help
    v:100219\s.zaglio: now print if @comment is null and delete if is '''';+help
    v:100129\s.zaglio: creation
    todo: using MS original methos is slow. Need to be rewritten
    t:
        create table test(t1 int,t2 int)
        exec sp__printf ''print comment of test''
        exec sp__comment ''test''
        exec sp__printf ''set comment of test''
        exec sp__comment ''test'',''test comment''
        exec sp__printf ''print comment of test''
        exec sp__comment ''test''
        exec sp__printf ''delete comment of test''
        exec sp__comment ''test'',''''  -- delete comment
        exec sp__printf ''print comment of test''
        exec sp__comment ''test''

        exec sp__comment ''[test]'',''test comment on table''
        exec sp__comment ''test.t1'',''test comment on t1'',@dbg=1
        exec sp__comment ''test.t2'',''test comment on t2''
        print dbo.fn__comment(''test'')
        select * from dbo.fn__comments(''test'')
        drop table test
    t:
        exec sp__comment ''sp__comment'',''test of sp comment'',@dbg=1
        exec sp__comment ''sp__comment.@path'',''name of object to comment'',@dbg=1
        print dbo.fn__comment(''sp__comment.@path'')
        exec sp__comment ''fn__comment.@path'',''name of object to comment'',@dbg=1
        print dbo.fn__comment(''fn__comment.@path'')

        select * from dbo.fn__comments(''sp__comment'')
*/
CREATE proc [dbo].[sp__comment]
    @path sysname=null,
    @comment nvarchar(4000)=null out,
    @dbg bit=0
as
begin
set nocount on
declare
    @proc sysname,
    @schema sysname,@sch_type sysname,
    @obj sysname,@obj_type sysname,
    @sub sysname,@sub_type sysname,
    @old_comment nvarchar(4000),
    @msg nvarchar(4000),@id int,
    @prop sysname,@xtype nvarchar(2)

select @proc=''sp__comment''

if @obj is null and @comment is null goto help

select @prop=prop,@schema=sch,@sch_type=sch_type,
       @obj_type=obj_type,@obj=obj,
       @sub_type=sub_type,@sub=sub
from dbo.fn__comment_types(@path)

if @dbg=1
    begin
    exec sp__printf ''prp=%s, sc=%s, tbl=%s, col=%s, @xt=%s, @id=%d'',
                    @prop,@schema,@obj,@sub,@xtype,@id
    exec sp__printf ''\t\t\t\tsct=%s, ttbl=%s, tcol=%s'',
                    @sch_type,@obj_type,@sub_type
    end

select @old_comment=convert(nvarchar(4000),value )
from fn_listextendedproperty (
    @prop,
    @sch_type, @schema,
    @obj_type, @obj,
    @sub_type, @sub);

if @comment is null begin select @comment=@old_comment exec sp__printf @old_comment goto ret end

if @comment=''''
    begin
    select @comment=@old_comment
    if not @comment is null
        begin
        exec sp__printf ''-- deleting comment "%s" for obj "%s"'',@comment,@obj
        exec sp_dropextendedproperty
            @name = @prop,
            @level0type = @sch_type, @level0name = @schema,
            @level1type = @obj_type, @level1name = @obj,
            @level2type = @sub_type, @level2name = @sub
        end
    goto ret
    end

if @old_comment is null
    exec sp_addextendedproperty
        @name = @prop, @value = @comment,
        @level0type = @sch_type, @level0name = @schema,
        @level1type = @obj_type, @level1name = @obj,
        @level2type = @sub_type, @level2name = @sub
else
    begin
    if @dbg=1 exec sp__printf ''-- replace comment "%s" of "%s"'',@old_comment,@obj
    exec sp_updateextendedproperty
        @name = @prop, @value = @comment,
        @level0type = @sch_type, @level0name = @schema,
        @level1type = @obj_type, @level1name = @obj,
        @level2type = @sub_type, @level2name = @sub
    end

goto ret

help:
select @msg =''Usage\n''
            +''\t@obj    can be table; table.column;(no sp,fn,view)''
exec sp__usage @proc,''
Parameters
    @obj    can be table; table.column;(no sp,fn,view)

Examples
    exec sp__comment ''''table'''',''''comment for table''''        -- se comment on table
    exec sp__comment ''''table.col'''',''''comment for column''''   -- se comment on column of table
    exec sp__comment ''''table'''',''''new comment''''              -- change comment
    exec sp__comment ''''table''''                              -- print comment
    exec sp__comment ''''table'''',''''''''                         -- remove comment

See also
    sp__into_tbl @tbl       show columns info of a table with comments

Note
    Actually is not possible get/set comments on view/proc/func
''

ret:
end -- proc sp__comment'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__comment:

-- ============================================================ sp__context_info
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__context_info',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120724
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__context_info') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__context_info') with nowait
        goto skip_sp__context_info
        end
    if @ver>120724
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__context_info') with nowait
        goto skip_sp__context_info
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__context_info') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__context_info'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__context_info]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120724\s.zaglio:changed structure
    v:111205\s.zaglio:set and reset context info
    t:sp__context_info @opt=''reset''
    t:sp__context_info ''test'',@dbg=1    -- d15f
    t:sp__context_info ''test1'',@dbg=1   -- 5310
    t:sp__context_info ''test'',@opt=''del'',@dbg=1
    t:select dbo.fn__context_info(''test''),dbo.fn__context_info(''test1'')
    t:select dbo.fn__context_info(''test2'')
*/
CREATE proc sp__context_info
    @val sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @val is null and @opt=''||'' goto help

-- ============================================================== declaration ==
declare
    @i      int,
    @l      int,
    @j      int,                        --
    @info   varchar(256),
    @cinfo  varbinary(128),
    @code   binary(2),@tcode binary(2), -- temporary code
    @del    bit                         -- del option
-- =========================================================== initialization ==
select
    @cinfo= isnull(context_info(),0),
    @del  = charindex(''|del|'',@opt),
    @code = dbo.fn__crc16(@val)

-- ======================================================== second params chk ==
if charindex(''|reset|'',@opt)>0
    begin
    select @cinfo=0
    set context_info @cinfo
    goto ret
    end

-- ===================================================================== body ==
-- scan cinfo and add or delete
select @i=1,@l=len(@cinfo),@j=0
while (@i<@l)
    begin
    if @j=0 and substring(@cinfo,@i,2)=0 select @j=@i
    if substring(@cinfo,@i,2)=@code break
    select @i=@i+2
    end

if @del=1 select @code=0
if @i<@l select @j=@i
select @cinfo=substring(@cinfo,1,@j-1)+@code+substring(@cinfo,@j+2,128)
set context_info @cinfo
if @dbg>0 print dbo.fn__hex(@cinfo)

dispose:
goto ret

-- =================================================================== errors ==
err_len:    exec @ret=sp__err ''context info is full'',@proc
            goto ret
-- ===================================================================== help ==
help:
select @info=dbo.fn__hex(context_info())

exec sp__usage @proc,''
Scope
    add or remove value to the process context info

Notes
    fn__context_info return the position if exists of zero.
    The position remain until somebody not delete and reinsert the value.

Parameters
    @val    string to codify and add to context info
    @opt    options
            del     remove the @val from context info
            reset   clean all context info

Examples
    exec sp__context_info "test"
    print dbo.fn__context_info("test")

-- Corrent context info is --
%p1%
'',@p1=@info
select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__context_info'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__context_info:

-- ==================================================================== sp__copy
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__copy',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100203
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__copy') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__copy') with nowait
        goto skip_sp__copy
        end
    if @ver>100203
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__copy') with nowait
        goto skip_sp__copy
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__copy') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__copy'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__copy]

begin try
exec dbo.sp_executesql @statement = N'/*  keep this for MS compatibility
    l:see LICENSE file
    g:utility
    v:100203\s.zaglio:  added -t in bcp call coz'' sql2k5 compatibility
    v:100202\s.zaglio:  added quotes and removed -t,-q in bcp call coz'' sql2k5 compatibility
    v:100111\s.zaglio:  a bug around local obj search
    v:091212\s.zaglio:  a different error report for bcp
    v:091209\s.zaglio:  added supporto for ## tables on bcp copy
    v:091206\s.zaglio:  added trust connection on bcp copy and specific error info on user rights
    v:091018\s.zaglio:  well tested version with bcp
    r:091015\s.zaglio:  added BCP extension. if @src/dsc end with .txt or .cvs is a file
    v:090812\s.zaglio:  global revision
    v:090713\S.Zaglio:  added fast restore from table_date_time to table
                        (disable triggers,not manage quoted names)
    v:090405\s.Zaglio:  revision
    v:090124\S.Zaglio:  create a local copy of a table (if @obj only specified) or copy to remote server
    t:sp__copy ''test'',@dbg=1      -- backup
    t:sp__copy ''test'',@b=1,@dbg=1 -- restore
    t:sp__copy ''ot04_stock'',''ot04_stock_uni'',@u=1,@v=2,@d=0,@uid=''sa'',@pwd='''',@dbg=1
    t:sp__copy_test_bcp
    t:
        create table test(id int identity,obj sysname)
        insert test select ''one''
        insert test select ''two''
        exec sp__copy ''test''
        drop table test
        drop table test_100111_1805
*/
CREATE proc [sp__copy]
    @src        sysname=null,       -- local obj  ( can accept wild chars as * & ?)
    @dst        sysname=null out,   -- duplicated obj or db or db.schema.obj or lnk.db or
    @r          bit=0,              -- restore from last backup (search for obj_date_time)
    @s          bit=0,              -- structure only
    @d          bit=1,              -- drop destination
    @t          bit=0,              -- enable trigger while copy
    @u          bit=0,              -- transform to unicode
    @v          tinyint=0,          -- activate verbose mode: 1=src->dst 2=more steps
    @uid        sysname=null,       -- necessary for recompiling on remote svr
    @pwd        sysname=null,       -- or on local server if we want a file compile
    @soe        bit=0,              -- stop on error
    @dbg        bit=0
as
begin
-- sp_find ''sp__copy'' -- used in:sp__copyalldata_to_db,sp__create_test_db,sp__distribute,sp_copy,sp_sync_distribute,sp_trk_adjust,sp_update_dev_data
/*  after hard work, ofcourse thanks to MS politics, something go wrong. SQLDMO will be dimessed
    and in a cluster of my customer some library was badly registered.
    So I decided to downgrade this sp to more standard form even if loose performance and backward compatibility

    sp__copy -> sp__script -> tmp table -> linked server -> sp__run_script -> drop local & remote tmp table
*/
set nocount on
declare
    @proc sysname,
    @msg nvarchar(4000),@tmp nvarchar(4000),@ret int,@line nvarchar(4000),
    @crlf nvarchar(2),@i int,@n int,@j int,@step int,
    @obj sysname,@obj_to sysname,
    @dt datetime,
    @bak sysname,
    @srv sysname,@db sysname,@schema sysname,@name sysname,
    @sql nvarchar(4000),@sql1 nvarchar(4000),@sql2 nvarchar(4000),@sql3 nvarchar(4000),
    @srv_sql nvarchar(512),@copy_sql nvarchar(512),
    @ot sysname,                    -- object type
    @oc_tbl smallint,   @oc_dri smallint,   @oc_trg smallint,
    @dbgdev bit,
    @ctofile bit,                   -- compile to file
    @srv_cur sysname,
    @db_cur sysname,
    @bcp_cmd nvarchar(1024),@bcp_op nvarchar(4),@file sysname,
    @temp nvarchar(512),
    @stype nvarchar(2),@dtype nvarchar(2),@ctype nvarchar(8),
    @exp1 bit,@exp2 bit,
    @end_declare bit

declare @objs table (id int identity,name sysname, type nchar(2))
declare @objd table (id int identity,name sysname, type nchar(2))

-- init and adjust
select
    @proc=''sp__copy'',
    @ret=0,
    @crlf=char(13)+char(10),
    @step=10,
    @oc_tbl=1,   -- table with owner and not chk
    @oc_dri=20,  -- pkey,idx and chk
    @oc_trg=32,  -- triggers only
    @dbgdev=0,
    @srv_cur=@@servername,
    @db_cur=db_name(),
    @end_declare=0

if not (@uid is null and @pwd is null)
    begin
    select @ctofile=1
    if @dbg=1 exec sp__printf ''-- will be compiled with osql''
    end

-- help
if @src is null goto help

if @dbg=1 exec sp__printf ''sp__copy @src=%s, @dst=%s'',@src,@dst

-- collect objects to copy
if charindex(''*'',@src)>0 or charindex(''?'',@src)>0
    insert into @objs (name,type)
    select name,xtype from sysobjects where name like replace(replace(replace(@src,''_'',''[_]''),''?'',''_''),''*'',''%'')
if charindex(''*'',@dst)>0 or charindex(''?'',@dst)>0
    insert into @objd (name,type)
    select name,xtype from sysobjects where name like replace(replace(replace(@dst,''_'',''[_]''),''?'',''_''),''*'',''%'')

select @exp1=0,@exp2=0
if right(@src,4) in (''.txt'',''.cvs'') or charindex(''\'',@src)>0 select @exp1=1
if right(@dst,4) in (''.txt'',''.cvs'') or charindex(''\'',@dst)>0 select @exp2=1

if @exp1=1 and @exp2=0
    begin
    if @dbg=1 exec sp__printf ''source is a file''
    insert @objs (name,type) select @src,''FL''
    if left(@dst,2)=''##''
        insert @objd (name,type) select @dst,''U''
    else
        insert @objd (name,type) select name,xtype from sysobjects where name=@dst
    if left(@dst,1)=''#'' and left(@dst,2)!=''##'' goto err_bcpnotmp
    end
if @exp1=0 and @exp2=1
    begin
    if @dbg=1 exec sp__printf ''destination is a file''
    if left(@src,1)=''#'' and left(@src,2)!=''##'' goto err_bcpnotmp
    if left(@src,2)=''##''
        insert @objs (name,type) select @src,''U''
    else
        insert @objs (name,type) select name,xtype from sysobjects where name=@src
    insert @objd (name,type) select @dst,''FL''
    end

-- local copy
if @exp1=0 and @exp2=0
    begin
    insert @objs (name,type) select name,xtype from sysobjects where name=@src
    insert @objd (name,type) select name,xtype from sysobjects where name=@src
    end

if @dbg=1 select * from @objs s left join @objd d on s.id=d.id

if not exists(select null from @objs) goto err_noobjs


create table #src (lno int identity(10,10),line nvarchar(4000))

-- for each @obj int @objs
select @i=min(id),@n=max(id) from @objs
while (@i<=@n) and (@ret=0 or @soe=0)
    begin
    select @obj=s.name,@stype=s.type,@obj_to=d.name,@dtype=d.type
    from @objs s left join @objd d on s.id=d.id where s.id=@i

    if @obj is null goto err_objnull
    if @obj_to is null goto err_bcpnotbl

    select @i=@i+1
    select @ot=dbo.fn__object_type(@obj)

    select @ctype=case
        when @stype=''FL'' and @dtype in (''U'')    then ''bcp<''
        when @stype in (''U'') and @dtype=''FL''    then ''bcp>''
        when @dst is null and @r=0              then ''bak''
        when @dst is null and @r=1              then ''res''
        else null
        end

    if @ctype is null goto err_ctype

    if @dbg=1 exec sp__printf ''sp__copy:@n=%d, @i=%d,@obj=%s,@obj_to=%s,@ctype=%s'',@n,@i,@obj,@obj_to,@ctype

    if @ctype in (''bcp>'',''bcp<'')
        begin
        -- if @db=''%db%'' or @svr=''%svr%''
        select
            @db=@db_cur,@srv=@srv_cur
            -- 091206: ,@uid=coalesce(@uid,''sa''),@pwd=coalesce(@pwd,'''')
        if @ctype=''bcp>'' select @bcp_op=''out'',@file=@obj_to,@obj=@obj
        else select @bcp_op=''in'',@file=@obj,@obj=@obj_to
        select @j=dbo.fn__charindex(''\'',@file,-1)
        if @j>0 select @temp=substring(@file,1,@j-1),@file=substring(@file,@j+1,4000)
        if @j=0 or @temp=''%temp%'' exec sp__get_temp_dir @temp out

        select
            @file=@temp+''\''+@file,
            @obj=case when left(@obj,1)=''#'' then @obj else @db+''..''+@obj end
        -- /t use space as terminator
        -- /n use native varchar
        -- /q SET QUOTED_IDENTIFIERS ON
        -- -C { ACP | OEM | RAW | code_page }
        -- -T (in upper case) trusted connection
        if @uid is null and @pwd is null
            select @bcp_cmd=''bcp "%db_table%" %op% "%file%" -S "%svr%" -c -T -CACP -t''
        else
            select @bcp_cmd=''bcp "%db_table%" %op% "%file%" -S "%svr%" -U "%uid%" -P "%pwd%" -c -CACP -t''
        exec sp__str_replace @bcp_cmd out,''%db_table%|%op%|%file%|%svr%|%uid%|%pwd%'',
                                          @obj,@bcp_op,@file,@srv,@uid,@pwd
        if @dbg=1 exec sp__printf @bcp_cmd
        insert into #src(line)
        exec @r=master..xp_cmdshell @bcp_cmd
        if @r=0 and exists(select null from #src where line like ''Error = %'') select @r=1
        if @r!=0
            begin
            exec sp__print_table ''#src''
            exec sp__printf @bcp_cmd
            goto err_bcp
            end
        continue
        end -- bcp

    -- backup/restore
    if @ctype in (''bak'',''res'')
        begin
        if @ctype=''res''
            begin
            select top 1 @bak=name
            from sysobjects
            where name like @obj+''[_]______[_]____''
            and isnumeric(right(name,4))=1
            and isnumeric(substring(name,len(name)-10,6))=1
            order by substring(name,len(name)-10,128) desc
            if @bak is null
                begin
                exec sp__printf ''-- no %s_yymmdd_hhnn found'',@obj
                continue
                end
            -- if found a table
            exec sp__printf ''-- restore %s -> %s '',@bak,@obj
            select @sql =''exec sp__triggers ''''da'''',''''''+@obj+'''''' ''+@crlf
                        +''truncate table ''+@obj+'' ''+@crlf
                        +''set identity_insert ''+@obj+'' on''+@crlf
                        +''insert into ''+@obj+'' select * from ''+@bak+'' ''+@crlf
                        +''set identity_insert ''+@obj+'' off''+@crlf
                        +''exec sp__triggers ''''ea'''',''''''+@obj+''''''''
            if @dbg=1 print @sql else exec (@sql)
            if @@error!=0 select @ret=1
            continue    -- next object
            end -- if @b=1

        -- backup mode
        set @dst=@obj+''_%t''
        set @dt=getdate()
        set @dst=replace(@dst,''%t'',convert(nvarchar(48),@dt,12)+''_''
                +right(''00''+convert(nvarchar(2),datepart(hh,@dt)),2)
                +right(''00''+convert(nvarchar(2),datepart(mi,@dt)),2))
        set @obj=dbo.fn__sql_quotename(@obj)
        set @dst=dbo.fn__sql_quotename(@dst)
        exec sp__printf ''-- copy %s -> %s '',@obj,@dst
        set @sql=''select * into ''+@dst+'' from ''+@obj+'' with (nolock) ''
        if @dbg=1 print @sql else exec(@sql)
        if @@error!=0 select @ret=1
        continue
        end -- backup mode

    -- copy mode
    if @v>0 exec sp__printf ''-- copy %s -> %s '',@obj,@dst

    /*
        1: parse names to determinate the form
        2: create the script of the source without trigger & index
            or with trigger&index if @t=1
        3: if @d=1 drop target
        4: rename or export structure
        5: if (only struct)@s=0 continue with next obj
        6: else copy data
    */
    exec sp__parse_name @dst,@srv out,@db out,@schema out,@name out
    if @srv=@@servername or @srv=dbo.fn__servername(null) select @srv=null
    if not @srv is null     -- check if remote (linked) svr exists
        begin
        if not exists (select * from master..sysservers where srvname=@srv and rpc=1)
            goto err_srv_or_rpc

        if @db is null select @db=db_name()
        -- check id remote db exists
        select @sql=''select @j=count(*) from ''+@srv+''.master..sysdatabases where name=''''''+@db+''''''''
        exec sp_executesql @sql,N''@n int out'',@j=@j
        if @j!=1 goto err_srv_db

        -- todo: check remote schema
        end

    -- if exist a db with same name of obj, the db win
    if  @srv is null and @db is null
    and exists (select * from master..sysdatabases where name=@name) select @db=@name,@name=null

    -- check locl db existance
    if  @srv is null and not @db is null
    and not exists (select * from master..sysdatabases where name=@db) goto err_loc_db

    -- todo: check local schema

    -- drop detination
    if @d=1
        begin
        if @dbg=1 exec sp__printf ''-- drop destination\nexec sp__drop ''''%s'''''',@name
        -- more secure check
        if @obj=@name goto err_same
        if @srv is null and @dbg=0 exec sp__drop @name
        if not @srv is null goto err_nods   -- todo
        end
    else
        begin
        if @obj=@name goto err_same
        if (@srv is null and @db is null) begin
            if dbo.fn__exists(@name,null)=1 goto err_dstexist
            end
        else
            goto err_dstchk
        end -- if @d=...

    -- create script
    if @dbg=1 exec sp__printf ''-- get script source''

    if @t=0
        exec sp__script @obj,''#src'',@step=@step,@oc=@oc_tbl  -- script without idx and trigger
    else
        begin
        exec sp__script @obj,''#src'',@step=@step,@oc=@oc_tbl
        exec sp__script @obj,''#src'',@step=@step,@oc=@oc_dri
        exec sp__script @obj,''#src'',@step=@step,@oc=@oc_trg
        end

    if @name!=@obj
        begin
        if @dbg=1 exec sp__printf ''-- rename''
        exec sp__script_replace @obj,@name,@step=@step              -- rename indexes, trigger...
        end

    if @dbgdev=1 select * from #src order by lno

    -- to unicode
    if @u=1
        begin
        if @dbg=1 exec sp__printf ''-- convert to unicode''
        exec sp__script_reduce @normalize=4
        end

    -- add change of db
    if not @db is null
        begin
        if @dbg=1 exec sp__printf ''-- add change of db''
        set identity_insert #src on
        insert into #src(lno,line) select 5,''use ''+@db
        set identity_insert #src off
        end

    -- if remote server, transfer src to dst server
    if not @srv is null
        begin
        select @srv_sql =''exec %srv%.%db%.%dbo%.sp_executesql ''
        exec sp__str_replace @srv_sql out,''%srv%|%db%|%dbo%|%tmp%'',@srv,@db,@schema,@tmp
        select @tmp=''tmp_''+replace(convert(sysname,newid()),''-'',''_'')
        select @sql =@srv_sql+
                    +''N''''create table %tmp% (lno int ,line nvarchar(4000))'''' ''
                    +''insert into %srv%.%db%.%dbo%.%tmp% select * from #src''
        exec sp__str_replace @sql out,''%srv%|%db%|%dbo%|%tmp%'',@srv,@db,@schema,@tmp
        if @dbg=1 print @sql else exec(@sql)
        end

    -- compile
    if @dbg=1
        exec sp__script ''#src''
    else
        begin
        exec @ret=sp__recompile ''#src'',
            @tofile=@ctofile,
            @srv=@srv_cur,
            @db=@db_cur,
            @uid=@uid,@pwd=@pwd
            -- ,@dbg=1
        if @ret!=0 goto err_comptbl
        end

    -- prepare sql for copy
    select @copy_sql    =''insert into ''
                        + case when not @srv is null
                          then ''%srv%.'' else '''' end
                        + case when not @db is null
                          then ''%db%.'' else '''' end
                        + case when not @schema is null
                          then ''%schema%.'' else '''' end
                        +''%name% ''
                        +''select * from %obj%''

    if @db is null select @sql=replace(@copy_sql,''%db%.'','''')
    exec sp__str_replace @copy_sql out,''%srv%|%db%|%schema%|%name%|%obj%'',@srv,@db,@schema,@name,@obj

    -- if is a table, load and run index and trigger
    if @ot=''U''
        begin
        -- if not structure only and not trigger while copy, copy data now
        if @s=0 and @t=0
            begin
            if @dbg=1 exec sp__printf ''-- copy data without idx and trg''
            if @dbg=1
                print @copy_sql
            else
                begin
                if @v>1 exec sp__printf ''-- coping data ...''
                exec(@copy_sql)
                end
            end

        if @dbg=1 exec sp__printf ''-- add idx and trg''

        truncate table #src
        if @dbgdev=1 select * from #src order by lno

        exec sp__script @obj,''#src'',@step=@step,@oc=@oc_dri
        exec sp__script @obj,''#src'',@step=@step,@oc=@oc_trg
        if @name!=@obj exec sp__script_replace @obj,@name,@step=@step

        if @u=1 exec sp__script_reduce @normalize=4     -- to unicode
        if @dbg=1
            exec sp__script ''#src''
        else
            begin
            exec @ret=sp__recompile ''#src'',
                @tofile=@ctofile,
                @srv=@srv_cur,
                @db=@db_cur,
                @uid=@uid,@pwd=@pwd
                -- ,@dbg=1
            if @ret!=0 goto err_compdri
            end

        -- finally copy data if with trigger on
        if @s=0 and @t=1
            begin
            if @dbg=1 print @copy_sql else
                begin
                if @v>1 exec sp__printf ''-- coping data ...''
                exec(@copy_sql)
                end
            end

        end -- if ot=''U''


    end -- while


drop table #src
goto ret

err_noobjs:     select @ret=-1 ,@msg=''#!no object found with this name/s'' goto ret
err_srv_or_rpc: select @ret=-2 ,@msg=''#!no dst server found or not support rpc'' goto ret
err_srv_db:     select @ret=-3 ,@msg=''#!no dst db found'' goto ret
err_loc_db:     select @ret=-4 ,@msg=''#!no local db found'' goto ret
err_nofc:       select @ret=-6 ,@msg=''#!file''''s compile not supported again'' goto ret
err_nods:       select @ret=-7 ,@msg=''#!remote drop not supported again'' goto ret
err_same:       select @ret=-8 ,@msg=''#!cannot copy on itself'' goto ret
err_dstexist:   select @ret=-9 ,@msg=''#!destination already exists, use @d=1'' goto ret
err_dstchk:     select @ret=-10,@msg=''#!remote destination existance not yet implemented'' goto ret
err_comptbl:    select @ret=-11,@msg=''#!compiling table'' goto ret
err_compdri:    select @ret=-12,@msg=''#!compiling dri'' goto ret
err_ctype:      select @ret=-13,@msg=''#!unk ctype'' goto ret
err_objnull:    select @ret=-14,@msg=''#!null object name'' goto ret
err_bcp:        select @ret=-15,@msg=''#!bcp has returned an error(NB: "unexpected EOF/Fine imprevista",''
                                    +''may depends on user''''s rights in trusted(-T) connection)'' goto ret
err_bcpnotbl:   select @ret=-16,@msg=''#!bcp require an existing dest. table'' goto ret
err_bcpnotmp:   select @ret=-17,@msg=''#!bcp cannot support temp tables'' goto ret

help:
select @msg =''Copy/rename a db object with or without data between dbs of linked servers\n''+
            +''Parameters:\n''
            +''\t@src    name of source object of local db\n''
            +''\t@dst    newname of local duplicate or db or svr.db or db.schema.name or\n''
            +''\t        svr.db.schema.newname\n''
            +''\nExamples\n''
            +''\tsp__copy ''''mytable'''',''''mytable.txt''''\t-- bcp to %temp%dir\file\n''
            +''\tsp__copy ''''c:\test\mytable.txt'''',''''mytable.txt''''\t-- bcp fromo file''

exec sp__usage @proc,@extra=@msg

ret:
if not @msg is null exec sp__printf @msg
return @ret
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__copy:

-- =============================================================== sp__copy_data
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__copy_data',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100517
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__copy_data') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__copy_data') with nowait
        goto skip_sp__copy_data
        end
    if @ver>100517
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__copy_data') with nowait
        goto skip_sp__copy_data
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__copy_data') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__copy_data'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__copy_data]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    g:utility
    v:100517\s.zaglio: changed @objs to #objs
    r:100513\s.zaglio: copy data from specular table of different databases
    todo:integrate into sp__copy)
    todo:now dtbls=stbls
*/
CREATE proc sp__copy_data
    @src sysname=null,
    @dst sysname=null,
    @truncate bit=0,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=''sp_copy_data'',@ret=0
if @src is null and @dst is null goto help

declare
    @sdb sysname,@ssch sysname,@stbl sysname,
    @ddb sysname,@dsch sysname,@dtbl sysname,
    @dflds nvarchar(4000), @sflds nvarchar(4000),
    @sql nvarchar(4000),@i int,@n int,
    @like sysname,
    @end_declare bit

create table #objs (id int identity,src sysname,dst sysname)

select @sdb=db,@ssch=sch,@stbl=obj from dbo.fn__parsename(@src,1,default)
select @ddb=db,@dsch=sch,@dtbl=obj from dbo.fn__parsename(@dst,1,default)

if charindex(''.'',@src)>0 and @sdb=@ddb goto err_pat

if charindex(''*'',@src)=0 insert #objs select @src,@dst
else
    begin
    -- select @sdb,@ssch,@stbl,@ddb,@dsch,@dtbl
    select @like=replace(dbo.fn__sql_unquotename(@stbl),''*'',''%'')
    select @sql=''
        use %sdb%
        insert #objs
        select "%sdb%"+"."+"%ssch%"+"."+[name],"%ddb%"+"."+"%dsch%"+"."+[name]
        from sysobjects
        where [name] like "%like%"
        and xtype="u"
    ''
    exec sp__str_replace @sql out,
        ''"|%sdb%|%ssch%|%ddb%|%dsch%|%like%'',
        '''''''',@sdb,@ssch,@ddb,@dsch,@like
    if @dbg=1 exec sp__printf ''%s'',@sql
    exec(@sql)
    end -- collect objs

create table #vars (id nvarchar(16),value sql_variant)
select @i=min(id),@n=max(id) from #objs

while (@i<=@n)
    begin
    select @src=src,@dst=dst from #objs where id=@i

    select @sdb=db,@ssch=sch,@stbl=obj from dbo.fn__parsename(@src,1,default)
    select @ddb=db,@dsch=sch,@dtbl=obj from dbo.fn__parsename(@dst,1,default)

    select @sflds=dbo.fn__flds_quotename(dbo.fn__flds_of(@stbl,'','',null),'','')
    select @dflds=dbo.fn__flds_quotename(@sflds,'','')

    truncate table #vars
    insert #vars values(''"'',        '''''''')
    insert #vars values(''%sdb%'',    @sdb)
    insert #vars values(''%ssch%'',   @ssch)
    insert #vars values(''%stbl%'',   @stbl)
    insert #vars values(''%ddb%'',    @ddb)
    insert #vars values(''%dsch%'',   @dsch)
    insert #vars values(''%dtbl%'',   @dtbl)
    insert #vars values(''%sflds%'',  @sflds)
    insert #vars values(''%dflds%'',  @dflds)
    if @truncate=1 insert #vars values(''%truncate%'', ''truncate table ''+@dsch+''.''+@dtbl)
    else insert #vars values(''%truncate%'','''')

    select @sql=''
    use %ddb%
    if objectproperty(object_id("%dsch%.%dtbl%"),"tablehasidentity")=1
        set identity_insert %dtbl% on
    %truncate%
    declare @rows bigint
    insert into %dsch%.%dtbl%(%dflds%)
    select %sflds%
    from %sdb%.%ssch%.%stbl%
    select @rows=@@rowcount
    exec sp__printf "-- %dtbl%:%d inserted",@rows
    if objectproperty(object_id("%dsch%.%dtbl%"),"tablehasidentity")=1
        set identity_insert %dtbl% off
    ''
    exec sp__str_replace @sql out,@tbl=1

    if @dbg=1 exec sp__printf ''%s'',@sql
    else exec(@sql)
    if @@error!=0 exec sp__printf ''%s'',@sql

    select @i=@i+1
    end -- while

drop table #vars

goto ret

err_pat:    exec @ret=sp__err ''I see a ''''.'''' but source and dest db are same; maybe forgot schema?'',@proc goto ret

help:
exec sp__usage ''sp__copy_data'',''
Parameters
    @src        source path with database.schema.tables (accept wildcard * on object)
    @dst        destination path with database (names are sames)
    @truncate   truncate dest table
    @dbg        show script but not execute

Examples
    sp__copy_data ''''source_db.dbo.obj'''',''''dest_db'''',@dbg=1,@truncate=1
    sp__copy_data ''''source_db.dbo.obj*'''',''''dest_db'''',@truncate=1,@dbg=1
    sp__copy_data ''''source_db..obj*'''',''''dest_db'''',@truncate=1,@dbg=1
''

select @ret=-1

ret:
return @ret
end -- sp__copy_data'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__copy_data:

-- ================================================================= sp__copy_db
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__copy_db',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120612
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__copy_db') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__copy_db') with nowait
        goto skip_sp__copy_db
        end
    if @ver>120612
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__copy_db') with nowait
        goto skip_sp__copy_db
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__copy_db') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__copy_db'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__copy_db]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120612\s.zaglio: added errors test and run option
    v:110324\s.zaglio: modern version
    v:100119\s.zaglio: do a backup and restore together
    t:sp__copy_db ''test'',@opt=''tmp:d:\'',@dbg=1
*/
CREATE proc sp__copy_db
    @to sysname=null,
    @from sysname=null,
    @opt sysname=1,
    @dbg bit=0
as
begin
declare @proc sysname,@ret int
select  @proc=object_name(@@procid),@ret=0,
        @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

declare @i int,@j int,@dev nvarchar(1024),@t datetime,@simul bit

if @to is null goto help
if @from is null select @from=db_name()

select @opt=replace(@opt,''|run|'',''|doit|'')   -- compatible with old ver.
select @dev=''%temp%''
select @i=charindex(''|tmp:'',@opt)+5,@j=charindex(''|'',@opt,@i)
if @dbg=1 exec sp__printf ''@opt=%s i=%d, j=%d'',@opt,@i,@j
if @i>5
    select @dev =substring(@opt,@i,@j-@i)
                +''%db%_%t%.bak''

if @dbg=1 exec sp__printf ''dev=%s'',@dev

exec sp__elapsed @t out,''init''
exec @ret=sp__backup @dev out,@from,@opt=@opt
if @ret=0
    begin
    exec sp__elapsed @t out,''after backup''
    exec sp__restore @to,@dev,@opt=@opt
    exec sp__elapsed @t out,''after restore''
    end

goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    duplicate a database into same server

Parameters
    @to     destination name (if not exists, will be created)
    @from   source db; if not specified current is used
    @opt    options
            run     secure code, disable default simulation
            tmp:??  uses ?? as alternative path that has more
                    free disk space

Examples
    sp__copy_db ''''test'''' -- copy this db to TEST db
''
select @ret=-1

ret:
return @ret
end -- sp__copy_db'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__copy_db:

-- =========================================================== sp__copy_test_bcp
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__copy_test_bcp',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__copy_test_bcp') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__copy_test_bcp') with nowait
        goto skip_sp__copy_test_bcp
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__copy_test_bcp') with nowait
        goto skip_sp__copy_test_bcp
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__copy_test_bcp') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__copy_test_bcp'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__copy_test_bcp]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091018\s.zaglio: test for bcp form of sp__copy
*/
CREATE proc sp__copy_test_bcp @dbg bit=0
as
begin
set nocount on
declare @r int
if dbo.fn__exists(''bcp_test'',null)=1 drop table bcp_test
declare @t sysname select @t=''a11_um''
-- sp__dir ''a11_um''
-- select * from a11_um
exec(''select top 0 * into bcp_test from ''+@t)
-- bcp do not manage difference between table and file without format
-- exec(''alter table bcp_test add ok bit'')
if @dbg=1 exec(''select * from ''+@t)
exec @r=sp__copy @t,''bcp_test.txt'',@dbg=1
if @r!=0 goto err
exec @r=sp__run_cmd ''type %temp%\bcp_test.txt''
if @r!=0 goto err
exec @r=sp__copy ''bcp_test.txt'',''bcp_testone'',@dbg=1
if @r!=-16 goto err
exec @r=sp__copy ''bcp_test.txt'',''bcp_test'',@dbg=1
if @r!=0 goto err

select * from bcp_test
drop table bcp_test
goto ret
err:
exec sp__printf ''sp__copy_test_bpc:bcp test error''
ret:
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__copy_test_bcp:

-- =================================================================== sp__count
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__count',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=111214
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__count') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__count') with nowait
        goto skip_sp__count
        end
    if @ver>111214
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__count') with nowait
        goto skip_sp__count
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__count') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__count'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__count]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:111214\s.zaglio: now @tables can use wild chars and restyled
    v:100221\s.zaglio: added @where
    v:091023\s.zaglio: added help
    v:090205\S.Zaglio: a little remake to better manage remote svr/db
    v:090127\S.Zaglio: added quotes around name because instance and - in some name
    v:090109\S.Zaglio: linked server extension only for table(not select)
    v:081020\S.Zaglio: count rows in select too
    v:081007\S.Zaglio: count rows in table
    t:begin declare @n int exec sp__count ''sysobjects'',@n out,@dbg=1 print @n end
    t:begin declare @n int exec sp__count ''select * from sysobjects where xtype=''''U'''''',@n out,@dbg=1 print @n end
    t:begin declare @n int exec sp__count ''sysobjects'',@n out,@svr=''loopback'',@dbg=1 print @n end
    t:begin declare @n int exec sp__count ''sysobjects'',@n out,@dbg=1,@where=''id<0'' print @n end
    t:begin declare @n int exec sp__count ''sys%'',@n out,@opt=''print'',@dbg=1 print @n end
*/
CREATE proc [dbo].[sp__count]
    @table nvarchar(4000)=null,
    @n bigint=null out,
    @where nvarchar(4000)=null,
    @opt sysname=null,
    @dbg bit=null
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @table is null goto help

-- ============================================================== declaration ==
declare
    @sql nvarchar(4000),
    @crlf nvarchar(2),
    @own sysname,@errl int,
    @svr sysname,@db sysname,
    @nn bigint,@params sysname,
    @print bit

declare @tbls table(obj sysname)

-- =========================================================== initialization ==

select
    @print=charindex(''|print|'',@opt),
    @n=0,
    @crlf=crlf,
    @svr=parsename(@table,4),
    @db=parsename(@table,3),
    @db=case when @svr is null then @db else isnull(@db,db_name()) end,
    @own=case when @db is null then null else isnull(parsename(@table,2),''dbo'') end,
    @svr=dbo.fn__sql_quotename(@svr),
    @db=dbo.fn__sql_quotename(@db),
    @params=N''@nn bigint out,@err int out''
from fn__sym()

-- ======================================================== second params chk ==
-- ===================================================================== body ==

-- simplify (n.b.: must converted into sp__sql_simplify or something similar. see also sp__count
select @table=replace(@table,@crlf,'' '')
select @table=rtrim(ltrim(@table))
select @table=replace(@table,''        '','' '')
select @table=replace(@table,''    '','' '')
select @table=replace(@table,''  '','' '')
select @table=replace(@table,''  '','' '')

if left(ltrim(@table),7)=''select ''
    insert @tbls select ''(''+@table+'') sq''
else
    insert @tbls(obj)
    select [name]
    from sysobjects
    where 1=1
    and xtype=''U''
    and [name] like @table

declare cs cursor local for
    select obj
    from @tbls
open cs
while 1=1
    begin
    fetch next from cs into @table
    if @@fetch_status!=0 break

    select @sql =''''
    select @sql =@sql+''select @nn=count(*) from ''+@table
                + coalesce('' where ''+@where,'''')+'' select @err=@@error''
    if not @db is null select @sql=''use ''+@db+'' ''+@sql

    if @dbg=1 exec sp__printsql @sql

    select @nn=0
    if @svr is null
        exec sp_executesql @sql,@params,@nn=@nn out,@err=@err out
    else
        begin
        select @sql =''exec ''+@svr+''.''+@db+''..sp_executesql N''''''
                    +dbo.fn__injectN(@sql)+'''''',N''''''+@params
                    +'''''',@nn=@nn out,@err=@err out''
        exec sp_executesql @sql,@params,@nn=@nn out,@err=@err out
        select @errl=@@error
        end
    if @err!=0 or @errl!=0 goto err_tbl
    if @print=1 exec sp__printf ''%s has %d rows'',@table,@nn
    select @n=@n+@nn

    end -- while of cursor
close cs
deallocate cs

goto ret

-- =================================================================== errors ==
err_tbl: exec @ret=sp__err ''near table "%s"'',@proc,@p1=@table goto ret
-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    count the numbers of lines of a local or remote set of tables

Parameters
    @table  is the table name; can be svr.db.dbo.tbl;
            can use % and _ but in this case the
            target svr.db must be a tween of this.

Options
    print   print the results
''
-- ===================================================================== exit ==
ret:
return @ret
end -- sp__count'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__count:

-- ============================================================== sp__csv_export
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__csv_export',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=151106
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__csv_export') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__csv_export') with nowait
        goto skip_sp__csv_export
        end
    if @ver>151106
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__csv_export') with nowait
        goto skip_sp__csv_export
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__csv_export') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__csv_export'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__csv_export]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:export,csv
    v:151106\s.zaglio: added collate database_default
    v:121118.1800\s.zaglio: a bug when #obj
    v:121117\s.zaglio: export a tbl/view to csv file
    t:exec sp__csv_export ''cfg'',''c:\temp\tbl.csv'',@dbg=1
*/
CREATE proc sp__csv_export
    @obj sysname = null,
    @path nvarchar(1024) = null,
    @where sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int  -- @ret: 0=OK -1=HELP, any=error id

-- ======================================================== params set/adjust ==

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end,
    @where=replace(nullif(@where,''''),''"'',''''''''),
    @path=nullif(@path,'''')

-- ============================================================== declaration ==
declare
    -- generic common
    -- @i int,@n int,                   -- index, counter
    @sql nvarchar(max),                 -- dynamic sql
    @flds nvarchar(4000),
    -- options
    @noq bit,                           -- no quotes
    @noh bit,                           -- no header
    @crlf nvarchar(2),
    @cn nvarchar(128),
    @temp nvarchar(4000),               -- temp path
    @line nvarchar(4000),
    @out bit,
    @obj_id int,
    @psep nchar(1),                     -- path separator
    @dt datetime,
    @end_declare bit

declare @cols table(
    pos int,col sysname,
    last bit,
    cc nvarchar(4000),                  -- convert cmd
    hdr sysname null,
    ct sysname null,                    -- column type
    cs nvarchar(4)                      -- convert style
    )

-- =========================================================== initialization ==
exec sp__get_temp_dir @temp out

select
    @dt=getdate(),
    @noq=charindex(''|noq|'',@opt),
    @noh=charindex(''|noh|'',@opt),
    @crlf=crlf,
    @path=replace(@path,''%temp%'',@temp),
    -- sp__format
    @path=replace(@path,''%dt%'',dbo.fn__format(@dt,''yyyymmdd_hhmmss'',default)),
    @path=replace(@path,''%yyyymmdd%'',convert(sysname,@dt,112)),
    @path=replace(@path,''%yymmdd%'',convert(sysname,@dt,12)),
    @path=replace(@path,''%hhmmss%'',dbo.fn__format(@dt,''hhmmss'',default)),
    @path=replace(@path,''%hhmm%'',dbo.fn__format(@dt,''hhmm'',default)),
    @out=isnull(object_id(''tempdb..#out''),0),
    @obj_id=isnull(object_id(case when left(@obj,1)=''#''
                             then ''tempdb..''+@obj
                             else @obj
                             end),0),
    @end_declare=1
-- select *
from fn__sym()

-- ======================================================== second params chk ==
if @obj is null and @path is null goto help

if @out=0
    create table #out(lno int identity primary key,line nvarchar(4000))

-- ===================================================================== body ==

if @obj_id=0 and @out=0     raiserror(''object "%s" not found'',16,1,@obj)
if @path is null and @out=0 raiserror(''path not specified'',16,1)
if right(@path,1)=@psep     raiserror(''path without file name'',16,1)
if @path is null and @obj_id=0 and @out=1
                            raiserror(''path or obj must specified'',16,1)

if @obj_id=0 goto file_write

exec sp__flds_list @flds out,@obj

if @flds is null raiserror(''no fields found for specified table'',16,1)

insert @cols(pos,col)
select pos,token
from dbo.fn__str_table(@flds,''|'')

update c set
    ct=token,
    cs=case when token like ''%date%'' collate database_default then '',126'' else '',0'' end
from @cols c
join dbo.fn__str_table(dbo.fn__flds_type_of(@obj,'','',null),'','') t
on c.pos=t.pos

select @cn=''convert(nvarchar(4000),isnull(''
update @cols set last=1 where pos=(select max(pos) from @cols)

update @cols
set cc =case @noq when 1 then '''' else ''''''"''''+'' end
       +case @noq when 1
        then @cn+col+'','''''''')''+cs+'')''
        else ''replace(''+@cn+col+'','''''''')''+cs+''),''''"'''',''''""'''')''
        end
       +case @noq when 1 then '''' else ''+''''"'''''' end,
    hdr=case @noq when 1 then '''' else ''"'' end
       +case @noq when 1
        then parsename(col,1)
        else replace(parsename(col,1),''"'',''""'')
        end
       +case @noq when 1 then '''' else ''"'' end

if @dbg=1 select * from @cols

if @noh=0
    begin
    select @line=null
    select @line=isnull(@line+'','','''')+hdr from @cols
    insert #out(line) select @line
    end -- header

-- compound the select
select @sql =isnull(@sql+''+'''',''''+''+@crlf,'''')+cc
from @cols
order by pos

if @sql is null raiserror(''bad inside code generation'',16,1)

select @sql =''insert #out(line)''+@crlf
            +''select ''+@crlf
            +@sql
            +@crlf+''as line ''
            +@crlf+''from [''+@obj+''] ''+@crlf
            +case when not @where is null
             then ''where ''+@where
             else ''''
             end

if @dbg>0 exec sp__printsql @sql

exec(@sql)
/*
drop table #out
sp__csv_export ''cfg'' -- error test
create table #out(lno int identity primary key,line nvarchar(4000))
select * into #cfg from cfg -- select * from #cfg
truncate table #out exec sp__csv_export ''#cfg'',@opt=''noh'' select * from #out
sp__csv_export @path=''%temp%\text.txt''

sp__csv_export ''cfg'',@path=''%temp%\text.txt'',@where=''[key] like "%"'',@dbg=1
xp_cmdshell ''type %temp%\text.txt''
xp_cmdshell ''del %temp%\text.txt''
*/

file_write:
if @dbg>0 select @path,* from #out
if not @path is null
    exec @ret=sp__file_write_stream @path,@fmt=''ascii'',@opt=''out''

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.
if @out=0 drop table #out
goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    export an object(table or view) to a text file, formatted as CSV
    (Comma Separated Values).

Notes
    when a comma is present in the value, automatically will be quoted "";
    when a " is present in the value, automatically will be doubled;
    default export format is ASCII

Parameters
    @obj    name of object to export; can be a #tbl
    @path   path of destination file; supporto macros:
            %temp%      value of cmd line environment
            %DT%        same of YYYYMMDD_HHMMSS
            %YYYYMMDD%  year month day
            %YYMMDD%    year month day
            %HHMMSS%    hour minutes seconds
            %HHMM%      hour minutes
    #out    if @path is null, insert lines into #out
            if @path is valued and @obj is null, save #out to @path
            create table #out(lno int identity primary key,line nvarchar(4000))
    @where  optional condition for @obj
    @opt    options
            noq     suppress quotes " around the value of each field
                    (if value will contain a , will cause a bad input)
            noh     suppress export of 1st line containing column names

Examples
    exec %proc% "tbl","%temp%\tbl.csv"
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end try
-- =================================================================== errors ==
begin catch
exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__csv_export'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__csv_export:

-- ============================================================== sp__csv_import
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__csv_import',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121205.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__csv_import') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__csv_import') with nowait
        goto skip_sp__csv_import
        end
    if @ver>121205.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__csv_import') with nowait
        goto skip_sp__csv_import
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__csv_import') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__csv_import'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__csv_import]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:import,csv
    o:130902\s.zaglio: sp__csvio
    o:130902\s.zaglio: sp__csvio_on
    v:121205.1000\s.zaglio: done test into table and #table
    r:121130\s.zaglio: improved import of bigger files
    r:121118\s.zaglio: load csv into a table
*/
CREATE proc sp__csv_import
    @path nvarchar(1024) = null,
    @tbl sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @e_msg nvarchar(2000)               -- used for big raiserror msgs

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end,
    @path=nullif(@path,'''')

-- ============================================================== declaration ==
declare
    @run bit,
    @tmp nvarchar(4000), @size int,
    @fmt sysname,
    @ls int,
    @l int,@i int,@row int,@col smallint,@j int,
    @cmd sysname,@hr int,@adodbstream int,
    @sep nvarchar(1),@q nvarchar(1),
    @qlf nvarchar(2),@qcr nvarchar(2),@qsep nvarchar(2),@qsp nvarchar(2),
    @txt nvarchar(max),@cc nvarchar(2),@qq nvarchar(2),
    @dq bit,                            -- double quote marker
    @ncols int,                         -- counter of max n cols
    @crlf nvarchar(2),
    @flds nvarchar(4000),
    @nflds int,
    @noh bit,
    @d datetime,
    @tbl_id int,                        -- test existance
    @end_declare bit

create table #sp__csv_import_fld(
    row int,col smallint,
    pos int,ln int,dq bit,
    primary key(row,col)
    )

create table #sp__csv_import_text(txt nvarchar(max))

-- =========================================================== initialization ==
select
    @noh=charindex(''|noh|'',@opt),
    @sep='','',@q=''"'',@qq=@q+@q,@qlf=@q+lf,@qcr=@q+cr,@qsep=@q+@sep,@qsp=@q+'' '',
    @ls=-1,
    @dq=0,
    @fmt=''utf-8'',
    @crlf=crlf,
    @tbl_id=case
            when left(@tbl,1)=''#''
            then object_id(''tempdb..''+quotename(@tbl))
            else object_id(quotename(@tbl))
            end,
    @flds=dbo.fn__flds_of(@tbl,'','',null),
    @ncols=0,@nflds=0,
    @end_declare=1
from fn__sym()
select @nflds=dbo.fn__str_count(@flds,'','')

-- ======================================================== second params chk ==
if @path is null goto help

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

/*
    @fmt    is the format of source file; by default is "utf-8"
            Other format are gived from constants acecpted by
            adodb.stream "charset" property.
    @ls     line separator (default -1 for CRLF else 10 for LF or 13 for CR)
            Unfortunatelly, MSSQL generates log that are incompatibile with
            this (or after too many tests I have not found one good)
            In that case the old xp_cmdshell "type ..." work well.
*/

-- drop table #buffer

if @path like ''%[%]temp[%]%''
    begin
    exec sp__get_temp_dir @tmp out
    select @path=replace(@path,''%temp%'',@tmp)
    if @dbg=1 exec sp__printf ''path:%s'',@path
    end

if @path like ''%..%'' raiserror(''worked protection against hackers :-)'',16,1)

select @size=4000

if @dbg=1 exec sp__elapsed @d out,''init stream''

select @cmd=''ADODB.Stream''
exec @hr = sp_oacreate @cmd, @adodbstream out
if @hr!=0 goto dispose
select @cmd=''Type''
exec @hr = sp_oasetproperty  @adodbstream ,@cmd,2 -- text
if @hr!=0 goto dispose
select @cmd=''charset''
exec @hr = sp_oasetproperty  @adodbstream ,@cmd,@fmt
if @hr!=0 goto dispose
select @cmd=''LineSeparator''
exec @hr = sp_oasetproperty  @adodbstream ,@cmd,@ls
if @hr!=0 goto dispose
select @cmd=''Open''
exec @hr = sp_oamethod  @adodbstream , @cmd, null
if @hr!=0 goto dispose
select @cmd=''LoadFromFile''
exec @hr = sp_oamethod  @adodbstream , @cmd, null, @path
if @hr!=0 goto dispose
select @cmd=''ReadText''

insert #sp__csv_import_text
exec @hr = sp_oamethod  @adodbstream , @cmd, null

if @hr!=0 goto dispose
exec @hr = sp_oadestroy @adodbstream
select @adodbstream=null

if @dbg=1 exec sp__elapsed @d out,''after load''

-- parse file
select top 1 @txt=txt,@l=len(@txt) from #sp__csv_import_text
select
    @i=charindex(@q,@txt)+1,    -- search first open quote
    @j=@i,
    @row=1,
    @col=1

if @i=1 raiserror(''no open quote found'',16,1)

if @dbg=1 exec sp__printf ''qsep=%s  l=%d  nflds=%s'',@qsep,@l,@nflds

while (@i<@l and @i>0)
    begin
    /*  1. load file into nvarchar(max)
        2. if format ",
        3. scan for 1st "
        4. scan for next " and take 2 chars
        5. if "" skip 2 and goto 4
        6. if ", store, next field, skip 2 and goto 4
        7. if "#13 or "#10 new line                                         */

    select @i=@i+1                  -- skip open quote
    select @i=charindex(@q,@txt,@i) -- search next quote close
    if @i=0 break;                  -- file must end with last quote
    select @cc=substring(@txt,@i,2)
    -- exec sp__printf ''r=%d c=%d j=%d i=%d, cc=%s'',@row,@col,@j,@i,@cc
    if @cc=@qq                      -- skip double quote
        begin
        select @i=@i+1,@dq=1
        continue                    -- skip inner quote
        end
    if @cc=@qsep or @cc=@qcr or @cc=@qlf or @cc=@qsp    -- new row
        begin
        insert #sp__csv_import_fld(row,col,pos,ln,dq)
        select @row,@col,@j,(@i-@j),@dq
        if @cc!=@qcr and @cc!=@qlf
            select @col=@col+1,@dq=0
        else
            begin
            -- exec sp__printf ''ncols=%d, col=%d'',@ncols,@col
            if @ncols<@col select @ncols=@col
            if @ncols!=@col
                raiserror(
                    ''different number of cols in row %d'',
                    16,1,@row
                    )
            if @row=1
                begin
                if @ncols>999
                    raiserror(''max 999 rows admitted'',16,1)
                if @nflds=0 select @nflds=@ncols
                if @nflds!=@ncols
                    begin
                    select @e_msg=''number of source cols(%d) ''
                                 +''differ from destination(%d)''
                    raiserror(@e_msg,16,1,@ncols,@nflds)
                    end
                end
            select @row=@row+1,@col=1,@dq=0
            end
        select @j=0
        end

    -- search next open quote
    select @i=@i+1                  -- skip close quote
    select @i=charindex(@q,@txt,@i) -- search next open quote
    select @j=@i+1                  -- mark start of field
    end -- scan loop

if @j>1 raiserror(''file do not correclty end with a quote'',16,1)

if @dbg=1 exec sp__elapsed @d out,''after parsing''

if @dbg>0
    select
        *,
        case dq when 0
        then substring(@txt,pos,ln)
        else replace(substring(@txt,pos,ln),@qq,@q)
        end as fld
    from #sp__csv_import_fld

-- sp__csv_import ''c:\shared_folders\backup_db\csv_test.txt'',@opt=''noh'',@dbg=1
-- drop table csv_test
-- sp__csv_import ''c:\shared_folders\backup_db\csv_test.txt'',@tbl=''csv_test''
-- select * from csv_test -- select * from #t
-- truncate table csv_test  -- drop table #t
-- select top 0 * into #t from csv_test --alter table #t drop column [last col]
-- truncate table #t
-- sp__csv_import ''c:\shared_folders\backup_db\csv_test.txt'',@tbl=''#t'',@dbg=1
if @flds is null
    select @flds=isnull(@flds+'','','''')
                +case @noh
                 when 0
                 then replace(substring(@txt,pos,ln),@qq,@q)
                 else ''c''+dbo.fn__format(col,''0'',len(@ncols))
                 end

    from #sp__csv_import_fld
    where row=1


-- Pivot table
select @txt=''
%insert%
select %alias%   -- [1] as a,[2] as b,...
%into%
from (
    select
        row,col,
        case dq when 0
        then substring(txt,pos,ln)
        else replace(substring(txt,pos,ln),''''''+@qq+'''''',''''''+@q+'''''')
        end as fld
    from #sp__csv_import_fld,#sp__csv_import_text
    ''+case @noh when 1 then '''' else ''where row>1'' end +''
    ) as src
pivot
(
    max(fld) for col in (%col%) -- [1],[2],...
) as result
''

select @txt=replace(@txt,
                    ''%alias%'',
                    dbo.fn__str_exp(''[%idx%] as [%%]'',@flds,'','')
                    )
select @txt=replace(@txt,''%col%'',dbo.fn__str_exp(''[%idx%]'',@flds,'',''))

if not @tbl is null
    begin
    if @tbl_id is null
        select @txt=replace(
                        replace(@txt,''%insert%'',''''),
                        ''%into%'',
                        ''into ''+@tbl
                        )
    else
        select @txt=replace(
                        replace(@txt,''%insert%'',''insert into ''+@tbl),
                        ''%into%'',
                        ''''
                        )
    end
else
    select @txt=replace(replace(@txt,''%insert%'',''''),''%into%'','''')

if @dbg=1 exec sp__printsql @txt
exec(@txt)

if @dbg=1 exec sp__elapsed @d out,''after pivot and out''

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.
if @hr!=0
    begin
    declare @source nvarchar(255)
    declare @description nvarchar(255)

    exec @hr = sp_oageterrorinfo @adodbstream, @source out, @description out
    raiserror(''ole error (%s;%s;%s)'',16,1,@cmd,@source,@description)
    end

if not @adodbstream is null
    begin
    exec @hr = sp_oadestroy @adodbstream
    select @adodbstream=null
    end

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    load a csv file and store it into a table

Notes
    - this replace old SP__CSV_IN
    - actually import only comma separated values closed into double quotes

Parameters
    @path   is the path of the file; if end with \ will loaded all .csv/.txt
    @tbl    is the name of destination table; if not exists will be created
            if not specified will used the name of file without extension
            can be a #temp table
    @opt    options
            noh     no header in the first line

See
    sp__csv_export

Examples
    sp__csv_import "%temp%\customers.txt
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
if not @adodbstream is null
    begin
    exec @hr = sp_oadestroy @adodbstream
    select @adodbstream=null
    end

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__csv_import'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__csv_import:

-- =================================================================== sp__csvio
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__csvio',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100509
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__csvio') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__csvio') with nowait
        goto skip_sp__csvio
        end
    if @ver>100509
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__csvio') with nowait
        goto skip_sp__csvio
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__csvio') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__csvio'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__csvio]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100509\s.zaglio: define structure for txt i/o
    todo: convert csvio into a tree structure
    t:
        sp__dir ''f:\sapshare''
        truncate table csvio
        sp__csvio
*/
CREATE proc [dbo].[sp__csvio]
    @tbl nvarchar(64)=null,
    @fld nvarchar(32)=null,
    @offset int=null,
    @len int=null,
    @info nvarchar(1024)=null,
    @rid sysname=0,
    @out sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=''sp__csvio'',@ret=0

if @tbl is null and @fld is null goto help

-- declaration
declare
    @tid tinyint,@rows int,@sql nvarchar(4000),
    @upd bigint,@ins bigint,@del bigint

-- init and addjust
if object_id(''csvio'') is null
    begin
    select @sql=''
    create table csvio(
        tid tinyint,
        id int identity primary key nonclustered,
        rid int not null,                   -- doc parent if this is a sub-segment
        des nvarchar(64) null,              -- tbl
        cod nvarchar(32) null,              -- fld
        flags int null,                     -- (not used)
        idx int not null,                   -- offset
        n   int not null,                   -- len
        at  datetime default (getdate()),   -- update, version
        dat nvarchar(1024) null             -- description
        )
    create clustered index csvio_tid on csvio (tid)''
    if @dbg=1 exec sp__printf ''%s'',@sql else exec(@sql)
    end -- create

select
    @tid=1,
    @out=dbo.fn__sql_unquotename(@out),
    @info=replace(@info,'''''''','''''''''''')

create table #vars (id nvarchar(16),value sql_variant)
insert #vars values(''"'',        '''''''')
insert #vars values(''%tid%'',    @tid)
insert #vars values(''%rid%'',    @rid)
insert #vars values(''%tbl%'',    @tbl)
insert #vars values(''%fld%'',    @fld)
insert #vars values(''%offset%'', @offset)
insert #vars values(''%len%'',    @len)
insert #vars values(''%info%'',   @info)
insert #vars values(''%out%'',    @out)

-- create out table
if not @tbl is null and @fld is null and @offset is null and @len is null and @info is null
and not @out is null
    begin
    select @sql=null
    select @sql=coalesce(@sql+'','','''')+quotename(cod)+'' nvarchar(''+convert(sysname,[n])+'')''
    from csvio
    where tid=@tid and rid=@rid
    and [des]=@tbl
    order by idx
    select @sql=''
    if not object_id("%out%") is null drop table [%out%]
    create table [''+@out+''](''+@sql+'')''
    end


-- list tbl fields
if not @tbl is null and @fld is null and @offset is null and @len is null and @info is null
and @out is null
    select @sql=''
        select des as tbl, cod as fld, idx as offset,n as [len], at as [updt],dat as info
        from csvio
        where tid=%tid% and rid=%rid%
        and des like "%tbl%%"
        order by des,idx
    ''


-- deletes


-- delete table
if not @tbl is null and @fld=''*'' and @offset is null
and @len is null and @info is null
    select @sql=''
    update csvio set tid=0 where tid=%tid% and rid=%rid% and des like "%tbl%%"
    select @del=@@rowcount
    ''

-- delete fields
if not @tbl is null and not @fld is null and @offset is null
and @len is null and @info is null
    select @sql=''
    update csvio set tid=0 where tid=%tid% and rid=%rid% and des="%tbl%" and cod like "%fld%%"
    select @del=@@rowcount
    ''

-- add/upd tbl/fld
if not @tbl is null and not @fld is null and not @offset is null
and not @len is null
    select @sql=''
        update csvio set idx=%offset%, n=%len%, dat="%info%"
        where tid=%tid% and rid=%rid% and des="%tbl%" and cod="%fld%"
        select @upd=@@rowcount
        if @upd=0
            begin
            insert csvio(tid,rid,des,cod,idx,n,dat)
            select %tid%,%rid%,"%tbl%","%fld%",%offset%,%len%,"%info%"
            select @ins=@@rowcount
            end
        ''


if not @sql is null
    begin
    exec sp__str_replace @sql out,@tbl=1
    if @dbg=1 exec sp__printf ''%s'',@sql
    else
        begin
        select @ins=0,@upd=0,@del=0
        exec sp_executesql @sql,
            N''@ins bigint out,@upd bigint out,@del bigint out'',
            @ins=@ins out,@upd=@upd out,@del=@del out
        if @out is null exec sp__printf ''-- %d rows inserted, %d updated,%d freed'',@ins,@upd,@del
        else exec sp__printf ''-- table dropped and recreated''
        end
    end


goto ret

help:
exec sp__usage @proc,''
Examples
    sp__csvio                                    -->this help
    sp__csvio @tbl                               -->list tbl fields
    sp__csvio @tbl,@fld,@offset,@len,@info       -- add or update
    sp__csvio @tbl,@fld                          -- delete
    sp__csvio @tbl,''''*''''                         -- delete table

    sp__csvio_in @path,@table,@definition

''

ret:
return @ret
end -- sp__csvio'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__csvio:

-- ================================================================ sp__csvio_in
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__csvio_in',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100509
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__csvio_in') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__csvio_in') with nowait
        goto skip_sp__csvio_in
        end
    if @ver>100509
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__csvio_in') with nowait
        goto skip_sp__csvio_in
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__csvio_in') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__csvio_in'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__csvio_in]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100509\s.zaglio: inport a txt file into a table with csvio
    t:sp__csvio_in ''c:\autoexec.bat'',''test''
*/
CREATE proc [dbo].[sp__csvio_in]
    @path   nvarchar(512)=null,
    @out    sysname=null,
    @def    sysname=null,
    @dbg    bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=''sp__csvio_in'',@ret=0

if @path is null and @out is null goto help

if @def is null select @def=@out

if not exists(select null from csvio where tid=1 and des=@def) goto err_def

-- declaration
declare
    @tid tinyint,
    @sql nvarchar(4000)

create table #src(lno int identity,line nvarchar(4000))

-- init and addjust

select
    @tid=1

create table #vars (id nvarchar(16),value sql_variant)
insert #vars values(''"'',        '''''''')

-- inport text (read utf8/unix/win txt files)
exec sp__file_read_stream @path,@out=''#src'',@dbg=@dbg

-- filter columns
select @sql=null
select @sql =coalesce(@sql+'','','''')+''substring(line,''+convert(sysname,idx)+'',''+convert(sysname,n)+'') ''
            +quotename(cod)
from csvio
where tid=@tid
and des=@def

if @out is null
    select @sql=''select ''+@sql+'' from #src order by lno''
else
    select @sql=''insert ''+@out+'' select ''+@sql+'' from #src order by lno''

if @dbg=1 exec sp__printf ''%s'',@sql
else exec(@sql)

drop table #src

goto ret

err_def:    exec @ret=sp__err ''definition "%s" not found'',@proc,@p1=@def    goto ret

help:
exec sp__usage @proc,''
''
select @ret=-1

ret:
return @ret
end -- sp__csvio'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__csvio_in:

-- ================================================================== sp__db_md5
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__db_md5',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090908
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__db_md5') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__db_md5') with nowait
        goto skip_sp__db_md5
        end
    if @ver>090908
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__db_md5') with nowait
        goto skip_sp__db_md5
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__db_md5') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__db_md5'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__db_md5]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090908/S.Zaglio: added some usefull query in comments
    v:081001/S.Zaglio: added calc of stored, function,view,triggers,
    v:080808/S.Zaglio: calculate hash md5 or entire list of table+colums def
    t:
    begin -- each call require 1 minute
    declare @m binary(16), @sql nvarchar(500)
    declare @dbg bit set @dbg=@dbg
    drop proc test_sp__db_md5
    drop table test_sp__db_md5
    print '''' print ''original md5 of all db and retest''
    exec sp__db_md5 @m out,@dbg=@dbg print dbo.fn_hex(@m)
    exec sp__db_md5 @m out,@dbg=@dbg print dbo.fn_hex(@m)
    -- test differencies
    print '''' print ''test add column to table''
    ALTER   table test_sp__db_md5 (a int)
    exec sp__db_md5 @m out,@dbg=@dbg print dbo.fn_hex(@m)
    ALTER  table test_sp__db_md5 add b bigint
    exec sp__db_md5 @m out,@dbg=@dbg print dbo.fn_hex(@m)
    drop table test_sp__db_md5
    print '''' print ''test after drop table''
    exec sp__db_md5 @m out,@dbg=@dbg print dbo.fn_hex(@m)

    -- test proc
    print '''' print ''test md5 all db after ALTER  proc''
    set @sql=''ALTER  proc test_sp__db_md5 as print 1'' exec(@sql)
    exec sp__db_md5 @m out,@dbg=@dbg print dbo.fn_hex(@m)
    print '''' print ''test md5 of single proc''
    set @sql=''alter proc test_sp__db_md5 as print 1'' exec(@sql)
    exec sp__db_md5 @m out,''test_sp__db_md5'',@dbg=@dbg print dbo.fn_hex(@m)
    print '''' print ''test alter proc to same but probably create change to alter''
    set @sql=''alter proc test_sp__db_md5 as print 1'' exec(@sql)
    exec sp__db_md5 @m out,''test_sp__db_md5'',@dbg=@dbg print dbo.fn_hex(@m)
    print '''' print ''test alter proc to same again''
    set @sql=''alter proc test_sp__db_md5 as print 1'' exec(@sql)
    exec sp__db_md5 @m out,''test_sp__db_md5'',@dbg=@dbg print dbo.fn_hex(@m)
    print '''' print ''test alter 1 nchar of same proc''
    set @sql=''alter proc test_sp__db_md5 as print 2'' exec(@sql)
    exec sp__db_md5 @m out,''test_sp__db_md5'',@dbg=@dbg print dbo.fn_hex(@m)
    drop proc test_sp__db_md5
    print '''' print ''test not existance after drop proc''
    exec sp__db_md5 @m out,''test_sp__db_md5'',@dbg=@dbg print dbo.fn_hex(@m)
    print '''' print ''original md5 of all db''
    exec sp__db_md5 @m out,@dbg=@dbg print dbo.fn_hex(@m)
    end
*/
CREATE  proc [dbo].[sp__db_md5]
    @db_md5 binary(16)=null out,
    @objects nvarchar(4000)=null,
    @dbg bit=0
as
begin
set nocount on
create table #objs ([id] int, [name] sysname, [xtype] nchar(2), md5 binary(16))
declare @n int
declare @i int set @i=1
declare @id int, @last_id int, @colid smallint
declare @obj sysname
declare @xtype nchar(2)
declare @output bit
if @db_md5 is null set @output=1
if @objects is null insert into #objs([id],[name],[xtype])
    select [id],[name],[xtype] from sysobjects where xtype in (''U'',''V'',''P'',''FN'',''TR'',''D'',''IF'',''TF'',''F'',''UQ'',''PK'',''C'',''RF'')
else begin
    set @n=dbo.fn__str_count(@objects,''|'')
    while (@i<=@n) begin
        set @obj=dbo.fn__str_at(@objects,''|'',@i)
        set @id=object_id(@obj)
        if @id is null and @dbg=@dbg print ''object ''+@obj+'' don''''t exists''
        else begin
            select @xtype=xtype from sysobjects where id=@id
            insert into #objs(id,name,xtype) values(@id,@obj,@xtype)
        end
        set @i=@i+1
    end -- while
end
if @dbg=@dbg begin
    select @n=count(*) from #objs
    print ''objects selected ''+convert(nvarchar(32),@n)
end
-- todo: indexes not pk,uq are excluded. Must be integreted
-- check tables
declare cst cursor local forward_only static read_only for
select    top 100 percent o.id, o.name as [name], o.name + ''|'' + c.name + ''|'' + t.name + ''|'' + convert(nvarchar(32), isnull(c.length, 0)) + ''|'' + convert(nvarchar(32),
          isnull(c.xprec, 0)) + ''|'' + convert(nvarchar(32), isnull(c.scale, 0)) as row
from      syscolumns c inner join
          #objs o on c.id = o.id inner join
          systypes t on c.xtype = t.xtype
where     o.xtype=''U''
order by o.id, c.colid
declare @a bigint,@b bigint, @c bigint, @d bigint
set @db_md5=null
set @last_id=null
declare @row nvarchar(4000)
open cst
while (1=1) begin
    fetch next from cst into @id,@obj,@row
    if @@error!=0 or @@fetch_status!=0 break
    if coalesce(@last_id,0)<>@id and @dbg=@dbg print ''calculating md5 of table ''+@obj
    if @last_id is null set @last_id=@id
    if @last_id<>@id set @last_id=@id
    exec sp__md5 @row,@db_md5 out,@a out,@b out,@c out,@d out
end -- while
close cst
deallocate cst
if @dbg=@dbg print ''calculate for other ojects''
-- check procs, funcs, views, etc.
declare csp cursor local forward_only static read_only for
select o.id,sc.colid,o.name
from syscomments sc
inner join #objs o on sc.id=o.id
where o.xtype != ''U''
order by sc.id,sc.colid
option (robust plan)
set @last_id=null
declare @row1 nvarchar(4000)
open csp
while (1=1) begin
-- Impossibile creare una riga di tabella di lavoro con dimensioni maggiori
-- della larghezza massima consentita. Rieseguire la query con l''hint ROBUST PLAN.
    fetch next from csp into @id,@colid,@obj
    if @@error!=0 or @@fetch_status!=0 break
    select @row=coalesce(substring([text],1,4000),''''),@row1=coalesce(substring([text],4001,4000),'''')
    from syscomments where id=@id and colid=@colid
    if coalesce(@last_id,0)<>@id and @dbg=@dbg print ''calculating md5 of object ''+@obj
    if @last_id is null set @last_id=@id
    if @last_id<>@id set @last_id=@id
    if len(@row1)=0
        exec sp__md5 @row,@db_md5 out,@a out,@b out,@c out,@d out
    else begin
        exec sp__md5 @row ,@db_md5 out,@a out,@b out,@c out,@d out
        exec sp__md5 @row1,@db_md5 out,@a out,@b out,@c out,@d out
    end
end -- while
close csp
deallocate csp
drop table #objs
if @output=1 select @db_md5
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__db_md5:

-- ================================================================== sp__delete
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__delete',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=081219
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__delete') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__delete') with nowait
        goto skip_sp__delete
        end
    if @ver>081219
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__delete') with nowait
        goto skip_sp__delete
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__delete') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__delete'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__delete]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:081219\S.Zaglio: delete a random row of a table
    t:sp__delete ''ot04_stock'',@random=1,@dbg=1  -- delete and reinsert a random line
    t:sp__delete ''as_f0005'',@random=1,@dbg=1  -- delete and reinsert a random line
    t:sp__delete ''as_f0101j2'',@where=''abat1="w"'',@random=1,@dbg=1  -- delete and reinsert a random line
*/
CREATE proc sp__delete
    @table sysname=null,
    @where nvarchar(4000)=null,
    @rows bigint=null out,
    @random bit=0,
    @dbg bit=0
as
begin
set nocount on
declare @crlf nvarchar(2) set @crlf=char(13)+char(10)
if @table is null begin
    exec sp__usage ''sp__delete''
    goto ret
end

declare    @from nvarchar(4000)
declare    @flds nvarchar(4000)
declare    @excludes nvarchar(4000)
declare    @col_sep nvarchar(8) set @col_sep=''|''
declare    @row_sep nvarchar(8)

if @row_sep is null set @row_sep=@crlf

declare @i int, @col int, @cols int
declare @sql nvarchar(4000)
declare @values nvarchar(4000)
declare @row nvarchar(4000)
declare @value sysname
declare @consts bit set @consts=0
declare @s sysname
if @where='''' set @where=null else set @where=replace(@where,''"'','''''''')
if @random=1 begin
    set @flds=dbo.fn__flds_of(@table,'','',@excludes)
    if @where is null begin
        set @sql=''SELECT top 1 ''+@flds+'' FROM ''+@table+'' ORDER BY newid() desc'' -- get a random line
    end
    else begin
        -- set @where=dbo.fn__inject(@where)
        if left(ltrim(@where),6)!=''where '' set @where='' where ''+@where
        set @sql=''SELECT top 1 ''+@flds+'' FROM ''+@table+'' ''+@where+'' ORDER BY newid() desc'' -- get a random line
    end
    set @values=''''
    exec sp__select @sql,@body=@values out,@null=''(/null/)'',@dtstyle=126,@dbg=@dbg
    set @values=dbo.fn__inject(@values)
    set @flds=replace(@flds,'','','' and '')
    set @flds=dbo.fn__str_exp(dbo.fn__str_exp(''%%=''''@@'''''',@flds,'' and ''),@values,''|'')
    set @where ='' WHERE ''+@flds
    set @where=replace(@where,''=''''(/null/)'''''','' is null '')
    set @sql=''DELETE FROM ''+@table+@where
    if @dbg=1 exec sp__printf @sql
    exec(@sql) set @rows=@@rowcount
    if @rows!=1 begin print ''no deleted row'' goto err end
    goto ret
end
else print ''no other extensions done''
goto ret
err:
ret:
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__delete:

-- =============================================================== sp__dependent
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__dependent',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130521.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__dependent') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__dependent') with nowait
        goto skip_sp__dependent
        end
    if @ver>130521.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__dependent') with nowait
        goto skip_sp__dependent
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__dependent') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__dependent'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__dependent]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:list,depend,object,dependencies,tree
    v:130521.1000\s.zaglio: print the tree of dependencies of an object
    t:sp__depends ''sp__script''
*/
CREATE proc sp__dependent
    @obj sysname = null,
    @level int = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
/*
    originally from
      http://stackoverflow.com/questions/379649/
           t-sql-puzzler-crawling-object-dependencies
*/
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @crlf nvarchar(2),
    @txt nvarchar(max),
    @sub sysname

-- =========================================================== initialization ==
select
    @level=isnull(@level+1,0),
    @crlf=crlf
from fn__sym()

-- ======================================================== second params chk ==
if @obj is null goto help

-- =============================================================== #tbls init ==
if object_id(''tempdb..#deptree'') is null
    create table #deptree(id int identity,lv int,obj sysname)

-- ===================================================================== body ==

-- print replicate('' '',@level) + @obj
insert #deptree values(@level,@obj)

declare cs cursor local for
    select
        distinct c.name
    from dbo.sysdepends a
        inner join dbo.sysobjects b on a.id = b.id
        inner join dbo.sysobjects c on a.depid = c.id
    where b.name = @obj
open cs
while 1=1
    begin
    fetch next from cs into @sub
    if @@fetch_status != 0 break
    -- if already marked, skip to avoid max recurse error
    if not exists(select top 1 null from #deptree where obj=@sub)
        exec sp__dependent @sub, @level
    end
close cs
deallocate cs

-- =============================================================== print tree ==

if @level=0
    begin
    select @txt=isnull(@txt+@crlf,'''')+replicate('' '',lv)+obj
    from #deptree
    exec sp__printsql @txt
    end


-- ================================================================== dispose ==
dispose:
if @level=0 drop table #deptree

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    print the dependency tree of the object, without repeat already printed
    objects

Parameters
    [param]     [desc]
    @obj        the object where find dependencies
    @level      used internally
    @opt        options
    @dbg        not used

Examples
    sp__depends "sp__script"
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__dependent'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__dependent:

-- ============================================================== sp__deprecated
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__deprecated',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140103
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__deprecated') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__deprecated') with nowait
        goto skip_sp__deprecated
        end
    if @ver>140103
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__deprecated') with nowait
        goto skip_sp__deprecated
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__deprecated') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__deprecated'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__deprecated]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:deprecate,list
    d:140127\s.zaglio:fn__street
    d:140127\s.zaglio:sp_info
    d:140103\s.zaglio:sp_find
    d:130806\s.zaglio:sp__parse
    d:130806\s.zaglio:sp__parse_test
    v:140103\s.zaglio:list only objects to deprecate
*/
CREATE proc sp__deprecated
as
begin
set nocount on
declare @proc sysname
select  @proc=object_name(@@procid)
-- ===================================================================== help ==
exec sp__usage @proc,''
Scope
    give only a list of generic (old) objects deprecated or obsolete

Notes
    Eventually you can describe the reason here.

Examples
    sp__parse,sp__parse_test:   never used, will be replaced by a generic parser
                                extracted from golden parser
''

-- ===================================================================== exit ==
ret:
return -1
end -- sp__deprecated'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__deprecated:

-- ===================================================================== sp__dir
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__dir',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131117.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__dir') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__dir') with nowait
        goto skip_sp__dir
        end
    if @ver>131117.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__dir') with nowait
        goto skip_sp__dir
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__dir') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__dir'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__dir]

begin try
exec dbo.sp_executesql @statement = N'/*  Leave this unchanged doe MS compatibility
    l:see LICENSE file
    g:utility
    v:131117.1000\s.zaglio:more detailed dbg info and enlarged @files.key size
    v:131021\s.zaglio:some correction near use of @kp
    v:131018\s.zaglio:corrected a bug if under mssql2k5
    v:131016.1000,131015,131014\s.zaglio: adding list of sub directory and unicode support
    v:130906,130904\s.zaglio: better help;added more debug info and (net) errors check
    v:130424,130227\s.zaglio: avoid #files when used with shortkey(* option);a small bug near full search
    v:130115,121116\s.zaglio: added grp: option;a bug near extra drop of #src
    v:121010\s.zaglio: in * opt, search _word% and word% added @isql
    v:120926,120919\s.zaglio: print instead of select and +select option;added # has wild char
    v:120918,120828,120209\s.zaglio: #files.size to bigint;added option *;remove #objs
    v:120112\s.zaglio: added read of format date from registry
    v:120111\s.zaglio: adapted to new #files format and a remake
    v:111103\s.zaglio: used fn__ntext_to_lines and dir when english settings
    v:110316\s.zaglio: adapted to last sp__write_ntext_to_lines upd
    v:100919.1115,100919.1100\s.zaglio: compatible with mssql2k;added special syntax
    v:100919.1000\s.zaglio: a bug in list of db objs and added out to #files
    v:100509,100508\s.zaglio: added @subdir;added DR flag and directory distinction
    v:100410,100405,100402\s.zaglio: added db search;added @out;a remake
    t:sp__dir ''*str*'',@dbg=1
    t:sp__dir ''%temp%\*.*'',@dbg=1
    t:sp__dir ''c:\*.ini'',@opt=''s'',@dbg=1    -- 16 sec by cmdline, 18 by sp
    t:sp__dir @opt=''*'',@path=''util''
    t:job
    t:sp__dir ''\\lupin\tecnico\_SCRIPTS\SINTESI\3\3.02''
*/
CREATE proc [dbo].[sp__dir]
    @path   nvarchar(512)   =null,
    @isql   nvarchar(1024)  =null,
    @opt    sysname         =null,
    @dbg    int             =0
as
begin
set nocount on
declare @proc sysname,@ret int
select  @proc=object_name(@@procid),@ret=0,
        @opt=dbo.fn__Str_quote(isnull(@opt,''''),''|'')

declare
    @cmd nvarchar(4000),@d datetime,
    @db sysname,@sch sysname,@obj sysname,
    @sql nvarchar(4000),@file nvarchar(4000),
    @n int,@i int, @path_ex sysname,
    @top sysname,@oby sysname,
    @psep nvarchar(2),@dsep nvarchar(2),
    @fmt sysname,@select bit,@grp bit,@s bit,
    @blob varbinary(max),@text nvarchar(max),
    @start int,@end int,@dir nvarchar(4000),
    @lng char,@rid int,@files_id int,
    @kp bit                         -- keep path in name

declare @obj_order table(xt varchar(4),ord tinyint)

insert @obj_order
select ''u'',10 union
select ''tr'',15 union
select ''p'',20 union
select ''v'',30 union
select ''if'',40 union
select ''tf'',50 union
select ''fn'',60 union
select ''sn'',70 union
select ''pk'',80 union
select ''f'',90 union
select ''d'',100

select
    @files_id=isnull(object_id(''tempdb..#files''),0),
    @dsep='':'',
    @select=charindex(''|select|'',@opt)|charindex(''|sel|'',@opt),
    @grp=charindex(''|grp|'',@opt),
    @s=charindex(''|s|'',@opt)|charindex(''|sub|'',@opt),
    @kp=charindex(''|kp|'',@opt)|1-@select|1-cast(@files_id as bit),
    @psep=psep,
    @path=case charindex(''|*|'',@opt)
          when 0
          then @path
          else replace(@path,''#'',''*'')+''*''
          end,
    -- full search
    @path_ex=case charindex(''|*|'',@opt)
           when 0 then ''''
           else ''*''+replace(@path,''#'',''*'')+''*''
           end
from fn__sym()

if left(@path,4)=''grp:'' select @grp=1,@path=substring(@path,5,len(@path)-4)

declare @stdout table (lno int identity primary key,line nvarchar(4000))

declare @dirs table(s int,e int,dir nvarchar(4000))

declare @files table (
    id int identity primary key,
    [key] nvarchar(446),sdt nvarchar(32),dt datetime,
    sfsize nvarchar(64) null ,n bigint null, flags smallint,
    rid int null
    )

declare @objs table (
    id int identity primary key,
    obj sysname,
    xtype nvarchar(2)
    )

if @path is null goto help

create table #src(lno int identity primary key,line nvarchar(4000))

if @grp=1
    begin
    select
        *
    from fn__script_info(default,''g'',0)
    where cast(val1 as sysname) like replace(@path,''*'',''%'')
    goto ret
    end

-- =========================================================== list from disk ==

if charindex(@dsep,@path)>0 or charindex(@psep,@path)>0
    begin

    -- temp file for output of dir
    exec sp__get_temp_dir @file out
    select @file=@file+@psep+replace(convert(sysname,newid()),''-'',''_'')

    if @dbg>0 exec sp__elapsed @d out

    -- this is the faster method found
    select @cmd =''cmd /u /c dir /4''+case @s when 1 then ''/s'' else '''' end
                                   +'' "''+@path+''" >''+@file+''.txt'' -- 11 secs x 250000
                                   +'' 2>''+@file+''.err''
    if @dbg>1 exec sp__printf ''%s'',@cmd
    delete from @stdout
    insert @stdout exec master..xp_cmdshell @cmd
    select @sql=null
    select top 1 @sql=line from @stdout where line is not null
    if not @sql is null goto err_dir

    if @dbg>0
        exec sp__elapsed @d out,''-- dir&err listed into %s in '',@v1=@file

-- ======================================================= load and split txt ==

    select @blob=null,@text=null
    select @sql=''select @blob=BulkColumn ''
               +''from openrowset(bulk ''''''+@file+''.txt'''', single_blob) as x''
    exec sp_executesql @sql,N''@blob varbinary(max) out'',@blob=@blob out

    select @text=cast(@blob as nvarchar(max))

    if @dbg>0 exec sp__elapsed @d out,''-- file TXT readed into memory''

    if @@error<>0
        exec sp__printf ''%s'',''>>> If error 4861, see http://msdn.microsoft.com/en-us/library/ms188365.aspx''

    truncate table #src
    insert #src(line) select line from fn__ntext_to_lines(@text,0)
    -- 111102\s.zaglio:deprecated sp__write_ntext_to_lines

    if @dbg>0 exec sp__elapsed @d out,''-- TXT splitted into lines''

    if @dbg>1
        begin
        select ''#src'' [#src],* from #src
        exec sp__elapsed @d out,''-- after show of #src''
        end

-- ======================================================= load and split err ==

    select @blob=null,@text=null
    select @sql=''select @blob=BulkColumn ''
               +''from openrowset(bulk ''''''+@file+''.err'''', single_blob) as x''
    exec sp_executesql @sql,N''@blob varbinary(max) out'',@blob=@blob out

    select @text=cast(@blob as nvarchar(max))

    if @dbg>0 exec sp__elapsed @d out,''-- file ERR readed into memory''

    if @@error<>0
        exec sp__printf ''%s'',''>>> If error 4861, see http://msdn.microsoft.com/en-us/library/ms188365.aspx''

    delete from @stdout
    insert @stdout(line) select line from fn__ntext_to_lines(@text,0)
    -- 111102\s.zaglio:deprecated sp__write_ntext_to_lines

    if @dbg>0 exec sp__elapsed @d out,''-- ERR splitted into lines''

    if @dbg>1 select ''stdout'' [stdout],* from @stdout
    select @sql=null
    select top 1 @sql=line from @stdout where line is not null
    if not @sql is null goto err_dir

-- ======================================================== delete temp files ==

    select @cmd=''del /q ''+@file+''.txt&del /q ''+@file+''.err''
    exec master..xp_cmdshell @cmd,no_output

-- ======================================================== split directories ==

    -- t:sp__dir ''c:\*.ini'',@opt=''s|select'' ,@dbg=1
    -- t:sp__dir ''c:\*.ini'',@opt=''s'' ,@dbg=1
    -- t:''sp__dir ''''i:\temp\*'''',@opt=''''s'''',@dbg=1''
    -- t:''sp__dir ''''c:\*.ini'''',@opt=''''s'''',@dbg=1''
    select @n=1
    if @s=0
        begin
        insert @dirs
        select min(lno),max(lno),
               case @kp when 1 then @path else '''' end from #src
        goto skip_with
        end

    -- mssql 2k5 do not support with under if
    ;with dirs(lno,dir) as (
        select lno,right(line,charindex('' '',reverse(line),
                                        len(line)-charindex(@psep,line))-1
                        ) dir
        from #src where line like '' %directory %''+@psep+''%''
        )
    insert @dirs(s,e,dir)
    select
        s.lno+1 s_lno,
        isnull((select top 1 lno from dirs e where e.lno>s.lno),
               (select max(lno) from #src))-1
        as e_lno,
        s.dir
    from dirs s
    select @n=@@rowcount
    if @dbg>0 exec sp__elapsed @d out,''-- %d direcotries'',@v1=@n

skip_with:

    if @dbg>0 exec sp__elapsed @d out,''-- tmp file deleted and split or dirs''

-- ============================================================= check region ==

    -- get local date format
    exec master.. xp_regread
            ''HKEY_CURRENT_USER'',''Control Panel\International'',
            ''sShortDate'',
            @fmt out
    -- or by cmd: REG QUERY "HKCU\Control Panel\International" /v sShortDate&
    -- exec master..xp_regenumvalues ''HKEY_CURRENT_USER'',''Control Panel\International''
    -- GRANT EXECUTE ON sys.xp cmdshell TO [BUILTIN\Users];

    /*  old solution by inspecting content
    if exists(
        select top 1 line
        from #src
        where substring(line,19,2) in (''PM'',''AM'') -- english setting
        )
        select @eng=1
    else
        select @eng=0
    */
    if @fmt like ''d%/m%/y%'' select @lng=''I''
    if @fmt like ''m%/d%/y%'' select @lng=''E''
    if @lng is null goto err_fmt

-- ====================================================== list files with dir ==

    -- 18 by sp, 17 without fn__dir_parse_list
    declare cs cursor local for
        select s,e,dir
        from @dirs
        where 1=1
    open cs
    while 1=1
        begin

        fetch next from cs into @start,@end,@dir
        if @@fetch_status!=0 break

        select @rid=0
        insert @files(rid,sdt,sfsize,[key])
        select @rid,sdt,sfsize,case @kp when 1 then @dir+''\''+name else name end
        from #src cross apply fn__dir_parse_list(line,@lng)
        where line like ''[0-9]%''
        and not line like ''%.''
        and not line is null
        and lno between @start and @end

        end -- cursor cs
    close cs
    deallocate cs

    if @dbg>2 select ''@files'' [@files],* from @files

    -- convert strings
    update @files set
        [n]=case
            when isnumeric(sfsize)=1
            then convert(bigint,sfsize)
            else null
            end,
        [dt]=convert(datetime,sdt),
        flags=case when sfsize=''<dir>'' then 32 else 0 end

    select @n=@@rowcount
    if @dbg>0 exec sp__elapsed @d out,''-- %d files parsed in'',@v1=@n

    if @dbg>2 select top 100 ''top 100 upd'' step,* from @files

    if @files_id=0 or charindex(''|*|'',@opt)>0
        begin
        if @select=1
            select
                [key] as path,
                flags,
                dt as creation_date,
                n as bytes
            from @files
            -- order by rid,[key]
        else
            begin
            -- insert #files([key],flags,dt,n)
            select id,[key],flags,dt,n
            into #files
            from @files
            -- order by rid,[key]
            exec sp__select_astext ''
                select
                    dt as creation_date,n as bytes,flags,[key] as path
                from #files
                order by path''
            end
        end
    else
        begin
        insert #files([key],flags,dt,n)
        select [key],flags,dt,n from @files
        -- create an index on name
        if not exists(
            select null from tempdb..sysindexes
            where id=@files_id and name=''#ix_files''
            )
            create index #ix_files on #files(rid,[key])
        end

    end     -- list files

-- ============================================================= list objects ==

else

    begin
    select @path=replace(@path,''_'',''[_]'')
    select @path=replace(@path,''%'',''[%]'')
    select @path=replace(@path,''?'',''_'')
    select @path=replace(@path,''*'',''%'')

    select @path_ex=replace(@path_ex,''_'',''[_]'')
    select @path_ex=replace(@path_ex,''%'',''[%]'')
    select @path_ex=replace(@path_ex,''?'',''_'')
    select @path_ex=replace(@path_ex,''*'',''%'')

    -- sp__dir @opt=''*'',@path=''file''
    -- exec sp__printf ''p:%s, e:%s'',@path,@path_ex
    -- sp__dir ''rep*'',''print ''''%obj%''''''

    insert @objs(obj,xtype)
    select [name],xtype
    from sysobjects
    where [name] like @path
    or [name] like @path_ex
    order by 2,1

    if isnull(@isql,'''')!=''''
        begin
        insert #src(line)
        select replace(@isql,''%obj%'',obj)
        from @objs
        order by isnull(
                    (select ord from @obj_order where xt=xtype),
                    ascii(xtype)
                 ), obj
        if @select=1 select line from #src order by lno
        else exec sp__print_table ''#src''
        end
    else
        begin
        if object_id(''tempdb..#files'') is null or charindex(''|*|'',@opt)>0
            begin
            if @select=1
                select * from @objs
                order by isnull(
                            (select ord from @obj_order where xt=xtype),
                            ascii(xtype)
                         ), obj
            else
                begin
                select identity(int,1,1) id,obj,xtype
                into #objs
                from @objs
                order by isnull(
                            (select ord from @obj_order where xt=xtype),
                            ascii(xtype)
                         ), obj

                exec sp__select_astext ''select * from #objs order by 1''
                drop table #objs
                end
            end
        else
            insert #files([key],flags)
            select
                obj,
                convert(smallint,cast(cast(xtype as varchar(2)) as binary(2)))
            from @objs
        end -- !@isql

    -- select @db=db,@sch=sch,@obj=obj from dbo.fn__parsename(@what,0,1)
    end -- db objs

dispose:
drop table #src
-- #files is dropped by engine
goto ret
-- =================================================================== errors ==
err_fmt:    exec @ret=sp__err ''unknown date setting %s'',@proc,@p1=@fmt goto ret
err_dir:    exec @ret=sp__err ''%s'',@proc,@p1=@sql                      goto ret
-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    list objects or files of a db or dir
    (sp__ftp uses the same #files format)

Notes
    I normally associate this SP to CTRL+4 as "sp__dir @opt=''''*'''',@path="

Parameters
    @path   can be a dir path or name of object of db
            store the list into #files if exists
            accept wild card *,? for both objects
            if begin with "grp:" or grp option is specified,
            search into group names of db objects

            create table #files (
                id int identity primary key,
                rid int default(0),     -- for subdirs
                [flags] smallint,       -- if &32=32 is a <DIR>
                [key] nvarchar(446),    -- obj name
                dt datetime,            -- creation date
                n bigint null           -- size in bytes
                )

    @isql   macro code that is printed instead of list where %obj% were replaced
    @opt    options
            *       consider automtically @path as "@path% or %[_]@path%"
                    to attach to a shortkey of SSMS; # can used as jolly char
            select  select instead of print results
            grp     see @path parameter info
            s       same as /s of dir, run recursivelly into sub directories
            sub     alias of option s
            kp      keep path in name (key)
    @dbg    1   show base info and statistics
            2   show also internal tables content

Notes
    where bytes is null, there is a <DIR>
    and index #ix_files will created on rid,key

Examples
    exec sp__dir ''''c:\test_if_exists''''

    create table #files ...
    exec sp__dir ''''c:\'''',@dbg=1
    select * from #files
    drop table #files

''
-- ===================================================================== exit ==

ret:

return @ret
end -- sp__dir'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__dir:

-- ======================================================== sp__dir_base_objects
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__dir_base_objects',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090124
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__dir_base_objects') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__dir_base_objects') with nowait
        goto skip_sp__dir_base_objects
        end
    if @ver>090124
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__dir_base_objects') with nowait
        goto skip_sp__dir_base_objects
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__dir_base_objects') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__dir_base_objects'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__dir_base_objects]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090124\S.Zaglio: list objects grouped by roots
*/
create proc sp__dir_base_objects
as
select a,b,c from
(
 select
    o.name,dbo.fn__str_at(o.name,''__'',1)+''_'' as a,
    dbo.fn__str_at(dbo.fn__str_at(o.name,''__'',2),''_'',1) as b,
    coalesce(dbo.fn__str_at(dbo.fn__str_at(o.name,''__'',2),''_'',2),'''') as c
 from sysobjects o
 where o.xtype in (''P'',''FN'',''U'',''V'') and left(o.name,3)!=''dt_'' -- and left(o.name,4)=''fn__''
) a
group by a,b,c
having c!=''''
union
select a,b,c from
(
 select
    o.name,
    dbo.fn__str_at(o.name,''_'',1) as a,
    substring(o.name,len(dbo.fn__str_at(o.name,''_'',1))+2,4000) as b,
    '''' as c
 from sysobjects o
 where o.xtype in (''P'',''FN'',''U'',''V'') and left(o.name,3)!=''dt_'' and not o.name like ''%[_][_]%''
) a
group by a,b,c
--having c!=''''
order by a,b,c'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__dir_base_objects:

-- ====================================================== sp__displayoaerrorinfo
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__displayoaerrorinfo',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=000000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__displayoaerrorinfo') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__displayoaerrorinfo') with nowait
        goto skip_sp__displayoaerrorinfo
        end
    if @ver>000000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__displayoaerrorinfo') with nowait
        goto skip_sp__displayoaerrorinfo
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__displayoaerrorinfo') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__displayoaerrorinfo'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__displayoaerrorinfo]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:000000\s.zaglio:used by sp_displaypkgerrors,execute_dts,run_dts
*/
CREATE  PROC sp__displayoaerrorinfo
    @object as int,
    @hr as int=0
AS

DECLARE @output nvarchar(255)
DECLARE @source nvarchar(255)
DECLARE @description nvarchar(255)

PRINT ''OLE Automation Error Information''

EXEC @hr = sp_OAGetErrorInfo @object, @source OUT, @description OUT
IF @hr = 0
    BEGIN
        SELECT @output = '' Source: '' + @source
        PRINT @output
        SELECT @output = '' Description: '' + @description
        PRINT @output
    END
ELSE
    BEGIN
        PRINT '' sp_OAGetErrorInfo failed.''
        RETURN
    END'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__displayoaerrorinfo:

-- ======================================================== sp__displayPKGErrors
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__displayPKGErrors',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__displayPKGErrors') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__displayPKGErrors') with nowait
        goto skip_sp__displayPKGErrors
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__displayPKGErrors') with nowait
        goto skip_sp__displayPKGErrors
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__displayPKGErrors') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__displayPKGErrors'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__displayPKGErrors]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091018\s.zaglio: display errors from spExecuteDTS execution
*/
CREATE PROC sp__displayPKGErrors
    @oPkg As integer
AS

SET NOCOUNT ON

DECLARE @StepCount int
DECLARE @Steps int
DECLARE @Step int
DECLARE @StepResult int
DECLARE @oPkgResult int
DECLARE @hr int

DECLARE @StepName nvarchar(255)
DECLARE @StepDescription nvarchar(255)

IF OBJECT_ID(''tempdb..#PkgResult'') IS NOT NULL
        DROP TABLE #PkgResult

CREATE TABLE #PkgResult
(
    StepName nvarchar(255) NOT NULL,
    StepDescription nvarchar(255) NOT NULL,
    Result bit NOT NULL
)

SELECT @oPkgResult = 0

EXEC @hr = sp_OAGetProperty @oPkg, ''Steps'', @Steps OUTPUT
IF @hr <> 0
BEGIN
        PRINT ''***  Unable to get steps''
        EXEC sp__displayoaerrorinfo @oPkg , @hr
        RETURN 1
END

EXEC @hr = sp_OAGetProperty @Steps, ''Count'', @StepCount OUTPUT
IF @hr <> 0
BEGIN
        PRINT ''***  Unable to get number of steps''
        EXEC sp__displayoaerrorinfo @Steps , @hr
        RETURN 1
END

WHILE @StepCount > 0
BEGIN
    EXEC @hr = sp_OAGetProperty @Steps, ''Item'', @Step OUTPUT, @StepCount
    IF @hr <> 0
    BEGIN
            PRINT ''***  Unable to get step''
            EXEC sp__displayoaerrorinfo @Steps , @hr
            RETURN 1
    END

    EXEC @hr = sp_OAGetProperty @Step, ''ExecutionResult'', @StepResult OUTPUT
    IF @hr <> 0
    BEGIN
            PRINT ''***  Unable to get ExecutionResult''
            EXEC sp__displayoaerrorinfo @Step , @hr
            RETURN 1
    END


    EXEC @hr = sp_OAGetProperty @Step, ''Name'', @StepName OUTPUT
    IF @hr <> 0
    BEGIN
            PRINT ''***  Unable to get step Name''
            EXEC sp__displayoaerrorinfo @Step , @hr
            RETURN 1
    END

    EXEC @hr = sp_OAGetProperty @Step, ''Description'', @StepDescription OUTPUT
    IF @hr <> 0
    BEGIN
            PRINT ''***  Unable to get step Description''
            EXEC sp__displayoaerrorinfo @Step , @hr
            RETURN 1
    END

    INSERT #PkgResult VALUES(@StepName, @StepDescription, @StepResult)
    PRINT ''Step '' + @StepName + '' ('' + @StepDescription + '') '' + CASE WHEN @StepResult = 0 THEN ''Succeeded'' ELSE ''Failed'' END

    SELECT @StepCount = @StepCount - 1
    SELECT @oPkgResult = @oPkgResult + @StepResult
END

SELECT * FROM #PkgResult

IF @oPkgResult > 0
BEGIN
    PRINT ''Package had '' + CAST(@oPkgResult as nvarchar) + '' failed step(s)''
    RETURN 9
END
ELSE
BEGIN
    PRINT ''Packge Succeeded''
    RETURN 0
END'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__displayPKGErrors:

-- ==================================================================== sp__drop
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__drop',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120802
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__drop') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__drop') with nowait
        goto skip_sp__drop
        end
    if @ver>120802
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__drop') with nowait
        goto skip_sp__drop
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__drop') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__drop'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__drop]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120802\s.zaglio: added drop of tbl.col
    v:110331\s.zaglio: removed use of fn__locked
    v:101112\s.zaglio: a bug near deleting tables in tempdb that''s not #temp tables
    v:100919\s.zaglio: added order by name desc to manage 1/2 problems with fkeys
    v:100718\s.zaglio: a total remake of old, to delete files,temps,any obj without errors
    t:
        declare @sql nvarchar(4000)
        select @sql=''
            create table drop_test1(a int)
            create table drop_test2(a int)
            create table drop_test3(a int)
            create table #drop_test4(a int)
            ''

        exec(@sql)  exec sp__printf ''multiple test''
        exec sp__drop ''drop_test1|drop_test2|drop_test3|#drop_test4''
        exec sp__dir ''drop_test*''

        exec(@sql)  exec sp__printf ''wild test''
        exec sp__drop ''drop_test*'',@dbg=1
        exec sp__dir ''drop_test*''
        exec sp__drop ''#drop_test4|drop_test*'',@simul=0
        exec sp__dir ''drop_test*''
        exec sp__drop ''#drop_test4'',@dbg=1
        select * from #drop_test4
*/
CREATE proc [dbo].[sp__drop]
    @names  nvarchar(4000)=null,
    @xtype  nvarchar(2)=null,
    @simul  bit=null,
    @opt    sysname=null,
    @dbg    int=null
as
begin
set nocount on
declare @proc sysname,@ret int
select
    @proc=object_name(@@procid),@ret=0,@dbg=isnull(@dbg,0),
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @names is null goto help
if @names=''*'' goto err_no

declare
    @i int,@n int,
    @obj nvarchar(512),@db sysname,@xt nvarchar(4),
    @sql nvarchar(4000),@sch sysname,
    @col sysname

-- select * from tempdb..sysobjects where name like ''%objs%'' and xtype=''u''
-- drop table #sp__drop_objs
create table #sp__drop_objs(
    id int identity,
    db sysname null,sch sysname null,
    obj nvarchar(512),
    col sysname null,
    xt nvarchar(4) null
    )


-- =================================== fill temp table with names

-- declare @names nvarchar(4000) select @names=''#t|[#tt]|here|[test]|[db].dbo.[test]|c:\test.txt|sintesi_vi00*|#sp__drop_objs''
-- declare @names nvarchar(4000) select @names=''drop_test3''
if charindex(''|'',@names)>0
    insert #sp__drop_objs(obj) select token from dbo.fn__str_params(@names,''|'',default)
else
    insert #sp__drop_objs(obj) select @names


-- =================================== update with db info & normalize name
update #sp__drop_objs set
    db= case
        when left(obj,1)=''#'' or left(obj,2)=''[#''
        then ''tempdb''
        when left(obj,1)!=''['' and (obj like ''%[\:/]%'')
        then '''' -- a file
        else isnull(parsename(obj,3),db_name())
        end,
    obj=case
        when left(obj,1)=''#'' or left(obj,2)=''[#''
        then parsename(obj,1)
        when left(obj,1)!=''['' and (obj like ''%[\:/]%'')  -- file
        then obj
        else case
             when charindex(''*'',obj)>0                  -- multiobj
             then replace(replace(parsename(obj,1),''_'',''[_]''),''*'',''%'')
             else
                case -- tbl.col
                when not object_id(parsename(obj,2),N''U'') is null
                then obj
                else parsename(obj,1)
                end
             end
        end

if exists(select null from #sp__drop_objs where obj in (''*'',''sp__drop'')) goto err_no


-- =================================== update with schema and type info from each db

-- declare @i int,@n int,@obj nvarchar(512),@db sysname,@sql nvarchar(4000),@dbg bit,@simul bit set @dbg=1
select @i=min(id),@n=max(id) from #sp__drop_objs
while (@i<=@n)
    begin
    select @db=db,@obj=obj from #sp__drop_objs where id=@i

    if charindex(''.'',@obj)>0
        begin
        select @col=parsename(@obj,1),@obj=parsename(@obj,2)
        select @sql =''alter table ''+quotename(@obj)+'' drop constraint ''
        select @sql = @sql + (select sys_obj.name as constraint_name
        from sysobjects sys_obj
        join syscomments sys_com on sys_obj.id = sys_com.id
        join sysobjects sys_objx on sys_obj.parent_obj = sys_objx.id
        join sysconstraints sys_con on sys_obj.id = sys_con.constid
        join syscolumns sys_col on sys_objx.id = sys_col.id
        and sys_con.colid = sys_col.colid
        where
        sys_obj.uid = user_id() and sys_obj.xtype = ''d''
        and sys_objx.name=@obj and sys_col.name=@col)
        if @dbg=0 exec(@sql) else exec sp__printsql @sql

        if exists(
            select null
            from syscolumns
            where id=object_id(@obj) and name=@col
            )
            begin
            select @sql =''alter table ''+quotename(@obj)
                        +'' drop column ''+quotename(@col)
            if @dbg=0 exec(@sql) else exec sp__printsql @sql
            end

        select @i=@i+1
        continue
        end -- drop tbl.col

    if @db!='''' -- not a file
    and exists(select null from master..sysdatabases where [name]=@db)
        begin

        if charindex(''%'',@obj)>0
            begin
            select  @simul=coalesce(@simul,1),
                    @sql=''insert #sp__drop_objs(db,sch,obj,xt)
                          select ''''%db%'''',u.[name],o.[name],o.xtype
                          from [%db%]..sysobjects o with (nolock)
                          join [%db%]..sysusers u with (nolock)
                          on o.uid=u.uid
                          where ''''S'''' not in (o.xtype,o.[type])
                          and o.name like ''''%obj%''''
                          order by o.name desc
                          ''
            end
        else
            begin
            if left(@obj,1)=''#''
                select  @sql=''
                              update objs set xt=o.xtype,sch=''''''''
                              from #sp__drop_objs objs
                              join [%db%]..sysobjects o with (nolock)
                              on o.id=object_id(''''%db%..%obj%'''')
                              where objs.id=%id%
                             ''
            else
                select  @sql=''
                              update objs set xt=o.xtype,sch=isnull(u.[name],'''''''')
                              from #sp__drop_objs objs
                              join [%db%]..sysobjects o with (nolock)
                              on o.name=''''%obj%'''' and ''''S'''' not in (o.xtype,o.[type])
                              join [%db%]..sysusers u with (nolock) on o.uid=u.uid
                              where objs.id=%id%
                             ''
            end

        exec sp__str_replace @sql out,''%db%|%obj%|%id%'',@db,@obj,@i
        if @dbg=1 exec sp__printf ''%s'',@sql
        exec(@sql)
        end -- db obj

    select @i=@i+1
    end -- expand

if @dbg=1 exec sp__select_astext ''select * from #sp__drop_objs order by obj''

if @simul=1 exec sp__printf ''-- use @simul=0 when delete multiple objects''


-- =================================== delete

-- declare @i int,@n int,@obj nvarchar(512),@db sysname,@sql nvarchar(4000),@xt sysname
--    declare @dbg bit,@sch sysname,@simul
--    select @dbg=1
select @i=min(id),@n=max(id) from #sp__drop_objs
while (@i<=@n)
    begin
    select @db=db,@sch=sch,@obj=obj,@xt=xt
    from #sp__drop_objs where id=@i
    if @db='''' -- is a file
        begin
        select @obj=dbo.fn__str_quote(@obj,''"'')
        set @sql=''del /q ''+@obj
        if @dbg=1 exec sp__printf @sql
        if @dbg=1 exec master..xp_cmdshell @sql else exec master..xp_cmdshell @sql,no_output
        end
    else
        begin
        if @db!='''' and not @xt is null
            begin
            -- exec sp__printf ''db=%s, sch=%s, obj=%s'',@db,@sch,@obj
            select @sql=
                case when @db!=db_name() and left(@obj,1)!=''#'' then ''use ''+quotename(@db)+'';'' else '''' end+
                ''drop ''+case
                when @xt=''TR''   then ''trigger ''
                when @xt=''SN''   then ''synonym ''
                when @xt=''U''    then ''table ''
                when @xt=''P''    then ''proc ''
                when @xt=''V''    then ''view ''
                when @xt in (''PK'',''UQ'',''F'')     then ''index ''
                when @xt in (''FN'',''IF'',''TF'')    then ''function ''
                else null
                end
                +
                case when @db=''tempdb''
                then quotename(@obj)
                else quotename(@sch)+''.''+quotename(@obj)
                end

            if @dbg=1 or @simul=1 exec sp__printf ''%s'',@sql
            else exec(@sql)

            end -- db obj

        if @sch is null and @xt is null and charindex(''%'',@obj)=0 and @dbg=1
            exec sp__printf ''obj "%s" not found'',@obj
        end -- file/db obj

    select @i=@i+1
    end --- while

drop table [#sp__drop_objs]

goto ret

err_no:     exec @ret=sp__err ''cannot delete all objects or sp__drop itself'',@proc  goto ret

help:

exec sp__usage @proc,''
Scope
    delete objects of any type

Parameters
    @names  can be a single object (with db), a file
            can use * wild chars for group of objs
            can use | to delete multiple objs (obj|obj|file|etc)
            can be a table.column tha has constraints to delete
    @xtypes not used
    @simul  when * is used, this must be specified eual to 1 do act
    @opt    options
''
select @ret=-1

ret:
return @ret
end -- sp__drop'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__drop:

-- ================================================================= sp__elapsed
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__elapsed',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130725
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__elapsed') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__elapsed') with nowait
        goto skip_sp__elapsed
        end
    if @ver>130725
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__elapsed') with nowait
        goto skip_sp__elapsed
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__elapsed') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__elapsed'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__elapsed]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130725,130724,130605\s.zaglio: +days;refactoring;removed printf deprecated parameters
    v:121012.1155\s.zaglio: excluded sp__elapsed time and changed behaviour for help
    v:111115\s.zaglio: replace %d/s with (null) and added help
    v:110526\s.zaglio: added variant ( at end of @c
    v:090916\s.zaglio: added minutes/secs/ms output format
    v:090629\S.Zaglio: added %t
    v:090626\S.Zaglio: added @v1,@v2,@v3,@v4
    v:090616\S.Zaglio: used sp__printf with force
    v:090122\S.Zaglio: print elapsed ms from @d to now
    c:in the future will calibrate it self to subtract inprocess time
    t:exec sp__elapsed ''2013-07-24T11:34:40.000'',''test(''
*/
CREATE proc [dbo].[sp__elapsed](
    @d datetime=null out,
    @c sysname=null,@ms int=null out,
    @v1 sql_variant=null,@v2 sql_variant=null,
    @v3 sql_variant=null,@v4 sql_variant=null
    )
as
begin
declare @proc sysname -- not set here to save time
declare @now datetime select @now=getdate()
declare @dd int, @hh int, @mm int,@ss int
declare @open nvarchar(1),@close nvarchar(1)
if @d=0
or (@@nestlevel=1 and @d is null) goto help

if not @c is null
    begin
    select @c=replace(@c,''%t'',convert(sysname,getdate(),126))
    -- if not @v1 is null or not @v2 is null or not @v3 is null or not @v4 is null
    select @c=dbo.fn__printf(@c,@v1,@v2,@v3,@v4,null,null,null,null,null,null)
    end

if not @c is null
    if right(@c,1)=''('' select @open='''',@close='')''
    else select @open='':'',@close=''''

if @d is null
    begin
    select @d=@now
    if not @c is null
        exec sp__printf ''%s%s%s%s'',
                        @c,@open,@d,@close
    end
else begin
    select @ms=datediff(ms,@d,@now)
    select @d=@now
    if not @c is null
        begin
        if @ms>86399999 -- days
            begin
            select @dd=@ms/86400000,@ms=@ms%86400000
            select @hh=@ms/3600000,@ms=@ms%3600000
            select @mm=@ms/60000,@ms=@ms%60000
            select @ss=@ms/1000,@ms=@ms%1000
            exec sp__printf ''%s%s%dd %dh %dm %ds %sms%s'',
                            @c,@open,@dd,@hh,@mm,@ss,@ms,@close
            /* if @hh=0 -- but not really necessary to loose perf.
                exec sp__printf ''%s%s24h %dm %ds %sms%s'',
                                @c,@open,@mm,@ss,@ms,@close */
            goto ret
            end
        if @ms>59999 -- mins
            begin
            select @mm=@ms/60000,@ms=@ms%60000
            select @ss=@ms/1000,@ms=@ms%1000
            exec sp__printf ''%s%s%dm %ds %sms%s'',
                            @c,@open,@mm,@ss,@ms,@close
            end
        else if @ms>999 -- secs
            begin
            select @ss=@ms/1000,@ms=@ms%1000
            exec sp__printf ''%s%s%ds %sms%s'',
                            @c,@open,@ss,@ms,@close
            end
        else
            exec sp__printf ''%s%s%dms%s'',
                            @c,@open,@ms,@close
        end
    end
ret:
select @d=getdate() -- to exclude sp__elapsed time
return 0

help:
select @proc=object_name(@@procid)
exec sp__usage @proc,''
Scope
    help trace times of application

Notes
    if run from a sp, work well for back compatibility

Parameters
    @d      is the last datetime elapsed
    @c      is an optional comment (if null do not print nothing)
    @ms     are the ms elapsed from @d
    @v1...  replace %s,%d in @c (use sp__printf)

Examples
    declare @d datetime,@ms int select @d=getdate()
    exec sp__elapsed @d out,@ms=@ms out
    print @ms waitfor delay "00:00:01"
    exec sp__elapsed @d out,"after example"

''

select @d=getdate() -- for compatibility of execution
return -1
end -- sp__elapsed'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__elapsed:

-- =================================================================== sp__email
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__email',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131223.1100
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__email') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__email') with nowait
        goto skip_sp__email
        end
    if @ver>131223.1100
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__email') with nowait
        goto skip_sp__email
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__email') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__email'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__email]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:131223.1100\s.zaglio: unified sense of attached data table
    r:130305\s.zaglio: small bug and better help
    r:130106\s.zaglio: added test of sp_email and no more mssql2k compatible
    r:120724\s.zaglio: added @header=1
    r:120723\s.zaglio: added chk err of sp__select_astext
    r:110831\s.zaglio: a bug near @dbg when printing @lbody and removed extra output
    r:110531\s.zaglio: now delete attached files if come from selects
    r:110324\s.zaglio: added chk of html type into lbody
    r:110306\s.zaglio: added faciliy #src in @attach
    r:110213\s.zaglio: a small possible bug near #src and @lbody and expanded to max
    r:100919\s.zaglio: compatibility with mssql2k
    r:100612\s.zaglio: added #src
    r:100404\s.zaglio: added cc,bcc
    r:100403\s.zaglio: managed attach as sql
    r:100228\s.zaglio: send a mail
    t:sp__config ''smtp_server'',''????''  <- specify your smtp server
    t:sp__email ''stefano.zaglio@seltris.it'',''test'',''test''
    t:
        exec sp__email @to=''stefano.zaglio@seltris.it'',@cc=''cc <stefano.zaglio@seltris.it>''
        exec sp__email @to=''stefano.zaglio@seltris.it'',@bcc=''bcc <stefano.zaglio@seltris.it>''
    t:
        create table #src(lno int identity,line nvarchar(4000))
        insert #src select ''line1''
        insert #src select ''line2''
        exec sp__email @to=''stefano.zaglio@seltris.it'',@attach=''#src''
        exec sp__email @to=''stefano.zaglio@seltris.it'',@body=''#src''
        drop table #src
*/
CREATE proc [dbo].[sp__email]
    @to nvarchar(4000) = null,
    @subj nvarchar(4000) = null,
    @body nvarchar(max) = null,
    @from nvarchar(100) = null ,
    @smtp nvarchar(100) = null,
    @attach nvarchar(4000) = null,
    @id int = null out,
    @err nvarchar(4000) = null out,
    @at sysname = null,
    @cc nvarchar(4000) = null,@bcc nvarchar(4000)=null,
    @dbg int=0
    /*********************************************************************

    this stored procedure takes the parameters and sends an e-mail.
    all the mail configurations are hard-coded in the stored procedure.
    comments are added to the stored procedure where necessary.
    references to the cdosys objects are at the following msdn web site:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdosys/html/_cdosys_messaging.asp

    http://support.microsoft.com/?scid=kb%3ben-us%3b312839&x=8&y=12
    ***********************************************************************/
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

declare
    @hr int,@imsg int,@cmd varchar(255),
    @src varchar(255),@des varchar(500),
    @i int,@n int,@crlf nchar(2),@bit bit,
    @htmlbody bit,
    @obj nvarchar(4000),@tmp nvarchar(4000),@path nvarchar(512),
    @dt datetime,@ptr varbinary(64),
    @sql nvarchar(4000),
    @sp_email int,                      -- object id for sp_email
    @event int                          -- returned by sp_email

declare
    @temail tinyint,
    @tsubj smallint,@tto smallint,@tbody smallint,@tfrom smallint,
    @tsch smallint,@tattach smallint,@tsts smallint

declare @attachments table(id int identity,[name] sysname,del bit default(0))

/*  an idea...
    obj ''email.subj'',@id=300 --> set sub id
    obj ''email.subj'',@rid=''email id'',@dat1=''subj'',@dat2=''attachments'',@dt=''insert time''
    obj ''email''             --> print below table
*/
select              --  rid         dat1            dat2                dt
    @temail =10,
    @tsubj  =300,   --  email id    subj            attachments         insert time
                    --  sched id    subj (when change and log is on ?)
    @tto    =301,   --  email id    -               to list             insert time
    @tbody  =302,   --  email id    -               body                insert time
    @tfrom  =303,   --  email id    from            -                   insert time
    @tattach=304,   --  email id    file/null       image file/select   insert time
    @tsch   =305,   --  email id    at param        -                   insert time
    @tsts   =306,   --  sched id    status/err nfo  -                   insert time

    @subj   =isnull(@subj,''(no subject)''),
    @body   =isnull(@body,''''),
    @from   =isnull(@from,''noreply@noreply.com''),
    @crlf   =crlf
from dbo.fn__sym()

if @to is null and @id is null goto help

create table #blob(blob image)
declare @stdout table(lno int identity primary key,line nvarchar(4000))
create table #sp__email_tmp(lno int identity primary key,line nvarchar(4000))

if @smtp is null
    begin
    select @smtp=convert(sysname,dbo.fn__config(''smtp_server'',null))
    if @smtp is null goto err_smtp
    end

-- todo: if begin with +, is a wiki text page

-- called by Agent (sp__email @rid=??)
if not @id is null -- is the id of email record
    begin
    goto err_todo
    goto send_email
    end -- reschedule

-- adjust data
if isdate(@at)=1 select @dt=convert(datetime,@at)
if patindex(@at,''%get%date%'')>0
    begin
    select @sql=''set @dt=''+@at
    exec sp_executesql @sql,N''@dt datetime out'',@dt=@dt out
    end

select @to=replace(@to,'','','';'')
select @to=replace(@to,''|'','';'')
select @to=replace(replace(@to,''['',''<''),'']'',''>'')
select @cc=replace(replace(@cc,''['',''<''),'']'',''>'')
select @bcc=replace(replace(@bcc,''['',''<''),'']'',''>'')

if @dbg>0
and (charindex(''@'',@to)=0 or charindex(''@'',isnull(@cc,''@''))=0)
    exec sp__printf ''-- %s: warning, @ absent in @to and/or @cc'',@proc

-- temp file for sql
exec sp__get_temp_dir @path out

if left(dbo.fn__sql_strip(substring(@body,1,32),null),7)=''select ''
    select  @attach=substring(@body,1,4000)+coalesce('';''+@attach,''''),
            @body=''see attachment''

if not object_id(@body) is null or not object_id(''tempdb..''+@body) is null
    begin
    select @obj=@body,@body=null
    if @obj!=''#src''
        begin
        exec sp__select_astext @obj,@out=''#sp__email_tmp'',@header=1
        select @body=isnull(@body,'''')+isnull(line,'''')+@crlf
        from #sp__email_tmp order by lno
        end
    else
        begin try
        exec sp_executesql N''
        select @body=isnull(@body,'''''''')+isnull(line,'''''''')+@crlf
        from #src order by lno'',N''@body nvarchar(max) out,@crlf nvarchar(4)'',
        @body=@body out,@crlf=@crlf
        end try
        begin catch
        -- print error_message()
        goto err_src
        end catch
    end

if substring(@body,1,6)=''<html>'' select @htmlbody=1

if @dbg=1
    begin
    exec sp__printf ''-- %s attach:%s'',@proc,@attach
    exec sp__printf ''-- %s body:'',@proc
    exec sp__printsql @body
    end

/*  explore attachments into @attachments and generate temp files to send
    in case there are particular attachments
*/
if not @attach is null
    begin
    -- test if want attach a table,view,sp recordset
    -- if charindex(''.'',@attach)=0 and dbo.fn__exists(@attach,null)=1

    select @i=1,@n=dbo.fn__str_count(@attach,'';'')
    while @i<=@n
        begin
        select @obj=dbo.fn__str_at(@attach,'';'',@i),@i=@i+1
        -- print dbo.fn__str_at(''select from order by t;c:\file'','';'',2)

        if @dbg=1 exec sp__printf ''attaching:%s'',@obj

        if not object_id(@obj) is null or not object_id(''tempdb..''+@obj) is null
            select @obj=''select * from ''+quotename(@obj)
        if left(dbo.fn__sql_strip(left(@obj,32),null),7)=''select ''
            begin   -- attach a result of a query stored into a temp xls file
            select @tmp=@path+''\''+replace(convert(sysname,newid()),''-'','''')+''.htm''
            if @dbg=1 exec sp__printf ''out to:%s'',@tmp
            exec @ret=sp__select_astext @obj,@out=@tmp,@dbg=0,@header=1
            if @ret!=0 goto err_sat
            insert @attachments([name],del) select @tmp,1
            end
        else
            begin -- attach files
            if charindex(''*'',@obj)>0 or charindex(''?'',@obj)>0
                begin   -- multiple files
                select @cmd=''dir /b "''+@obj+''"''
                delete from @stdout
                insert @stdout exec master..xp_cmdshell @cmd
                insert @attachments([name]) select line from @stdout
                end
            else        -- single file
                insert @attachments([name]) select @obj
            end -- attach files

        end -- while

    end -- attach

-- ##########################
-- ##
-- ## SP_EMAIL integration
-- ##
-- ########################################################
-- goto send_email
select @sp_email=object_id(''SP_EMAIL'')
if not @sp_email is null
    begin

    -- the sp_email must have same parameters + @event, all out
    if ((select count(*)+1
         from syscolumns
         where id=object_id(@proc)
        )
        =
        (select count(*)
         -- select [sp_email].*
         from syscolumns [sp_email]
         left join syscolumns [sp__email]
         on [sp__email].id=object_id(@proc)
         and [sp_email].name=isnull([sp__email].name,''@event'')
         where [sp_email].id=@sp_email and [sp_email].isoutparam=1
        )
       )
        begin
        if @dbg=1 exec sp__printf ''-- %s: redirect to sp_email'',@proc
        exec @ret=sp_email
            @event=@event out,
            @to =@to out,
            @subj=@subj out,
            @body=@body out,
            @from=@from out,
            @smtp=@smtp out,
            @attach=@attach out,
            @err=@err out,
            @at=@at out,
            @id=@id out,
            @cc=@cc out,
            @bcc=@bcc out,
            @dbg=@dbg out
        if @event!=0 goto ret
        if @event is null goto err_evt
        if @dbg=1 exec sp__printf ''-- %s: continue from sp_email'',@proc
        end
    else
        begin
        if @dbg=1
            exec sp__printf ''-- %s:sp_email exists but with different sign'',
                            @proc
        end
    end -- sp_email

send_email:

-- if scheduled mail, load all attachment into table
if not @at is null
    begin
    select @i=min(id),@n=max(id) from @attachments
    while (@i<=@n)
        begin
        select @obj=[name] from @attachments where id=@i
        -- load file into blob
        exec sp__file_read_blob @obj,@out=''#blob.blob''
        -- select @tattach,@rid,left(@attach,256),blob from #blob
        truncate table #blob
        select @i=@i+1
        end
    -- exec sp__job @run=''exec sp__email @id=%d'',@at=@at,@p1=@id
    /*
        instead of use sp__job
        better use directly use of jobs proc and schedule a new send after each sent
    */
    end -- load into tmp

--************* create the cdo.message object ************************
select @cmd=''cdo.message''
exec @hr = sp_oacreate @cmd, @imsg out
if @hr <>0 goto err_obj

--***************configuring the message object ******************
-- this is to configure a remote smtp server.
-- http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdosys/html/_cdosys_schema_configuration_sendusing.asp
select @cmd=''configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendusing").value''
exec @hr = sp_oasetproperty @imsg, @cmd,''2''
if @hr <>0 goto err_obj

-- this is to configure the server name or ip address.
-- replace mailservername by the name or ip of your smtp server.
select @cmd=''configuration.fields("http://schemas.microsoft.com/cdo/configuration/smtpserver").value''
exec @hr = sp_oasetproperty @imsg,@cmd, @smtp
if @hr <>0 goto err_obj

-- save the configurations to the message object.
select @cmd=''configuration.fields.update''
exec @hr = sp_oamethod @imsg, @cmd, null
if @hr <>0 goto err_obj

-- set the e-mail parameters.
select @cmd=''To''
exec @hr = sp_oasetproperty @imsg, @cmd, @to
if @hr <>0 goto err_obj

select @cmd=''CC''
exec @hr = sp_oasetproperty @imsg, @cmd, @cc
if @hr <>0 goto err_obj

select @cmd=''BCC''
exec @hr = sp_oasetproperty @imsg, @cmd, @bcc
if @hr <>0 goto err_obj

select @cmd=''From''
exec @hr = sp_oasetproperty @imsg, @cmd, @from
if @hr <>0 goto err_obj

select @cmd=''Subject''
exec @hr = sp_oasetproperty @imsg, @cmd, @subj
if @hr <>0 goto err_obj

if exists(select null from @attachments)
    begin
    declare @hattach int
    select @i=min(id),@n=max(id) from @attachments
    select @cmd=''AddAttachment''
    while (@i<=@n)
        begin
        select @obj=[name],@bit=del from @attachments where id=@i
        select @hattach=null
        exec @hr = sp_oamethod @imsg, @cmd, @hattach out, @obj, null

        if @bit=1
            begin
            select @sql=''del /q "''+@obj+''"''
            exec master..xp_cmdshell @sql,no_output
            end
        if @hr <>0 goto err_obj

        select @i=@i+1
        end -- while
    end -- attach

-- if you are using html e-mail, use ''htmlbody'' instead of ''textbody''.
if @htmlbody=1 select @cmd=''HTMLBody'' else select @cmd=''TextBody''

exec @hr = sp_oasetproperty @imsg, @cmd, @body
if @hr <>0 goto err_obj

select @cmd=''Send''
exec @hr = sp_oamethod @imsg, @cmd, null
if @hr <>0 goto err_obj

if @dbg=1 exec sp__printf ''-- %s: email sent through "%s"'',@proc,@smtp

exec @hr=sp_oadestroy @imsg
select @imsg=null
if @hr <>0 goto err_obj

dispose:
if not object_id(''tempdb..#sp__email_tmp'') is null drop table #sp__email_tmp
if not object_id(''tempdb..#blob'') is null drop table #blob

goto ret

-- =================================================================== errors ==

err_obj:
exec @hr = sp_oageterrorinfo @imsg, @src out, @des out
select @err=@cmd+'':''+coalesce(@src,'''')+'';''+coalesce(@des,'''')
exec @ret=sp__err @err,@proc
if not @imsg is null exec @hr=sp_oadestroy @imsg
goto ret

err_smtp:
select @sql=''set at least the smtp server with\n''
           +''\tsp__config(''''smtp_server'''',''''???'''')''
exec @ret=sp__err @sql,@proc
select @err=''no smtp server''
goto ret

err_todo:
exec @ret=sp__err ''todo'',@proc
goto ret

err_sat:
exec sp__err ''sp__select_astext error from sp__email'',@proc
goto ret

err_evt:
exec sp__err ''sp_email did not handle @event'',@proc
goto ret

err_src:
exec sp__err ''bad #src format, see help'',@proc
goto ret

-- ===================================================================== help ==

help:
create table #vars (id nvarchar(16),value sql_variant)
insert #vars values(
    ''%smtp_server%'',dbo.fn__config(''smtp_server'',null)
    )
insert #vars values(
    ''%smtp_from%'',coalesce(dbo.fn__config(''smtp_from'',null),@from)
    )

select @tmp=''@event = @event out''
select @tmp=@tmp+'',''+@crlf+''        ''+name+'' = ''+name+'' out''
--select name -- sp__email
from syscolumns
where id=object_id(@proc)
order by colid

exec sp__usage @proc,''
Scope
    Send an email using the CDO.MESSAGE object.

Notes
    before do that, test the presence of SP_EMAIL and call it;
    if SP_EMAIL @event return a non zero value, exit without process
    because means that SP_EMAIL has managed data;
    SP_EMAIL must have same parameters all with out clause;

Parameters:
    @to     destination
    @subj   subject
    @from   by default is noreply@noreply.com
    @smtp   smtp server or ip (see global variables below)
    @err    returned error string by cdo.message.send command
    @at     (todo) schedule time
    @cc     carbon copy
    @bcc    black carbon copy
    @body   can be a text constant or a temp table name or a query
            if is a temp table, the content will be inserted into the body
            if is a query, the content will be attached as a HTML table file
    @attach can be single o multiple files separated by ;
            and wild card *,? are valid
            can be a query or a temp table
    @every  YYYYMMDD.HHmmSS or ... (todo)
    @dbg    not zero, show debug info

Glabal variables:
    With dbo.fn__global_set(''''variable'''',''''value'''')
    can be configured global variables for
        smtp_server     (actual:%smtp_server%)
        smtp_from       (actual:%smtp_from%)

Aids
    create table #src(lno int identity,line nvarchar(4000))
    exec sp_email
        %p1%
'',@p1=@tmp
select @ret=-1
drop table #vars

-- ===================================================================== exit ==

ret:
return @ret
end -- sp__email'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__email:

-- =============================================================== sp__email_job
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__email_job',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120101
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__email_job') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__email_job') with nowait
        goto skip_sp__email_job
        end
    if @ver>120101
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__email_job') with nowait
        goto skip_sp__email_job
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__email_job') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__email_job'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__email_job]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:deferred,at,time,date
    r:120101\s.zaglio: manage deferred emails
*/
CREATE proc sp__email_job
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
        |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if  @run=0 goto help


-- =============================================================== #tbls init ==

-- ===================================================================== body ==

/*
tid         flags   rid         pid     cod         dt
email       type    rply        fwd     null
to                  email       copy    txt
subj                email       copy    txt
body                email       copy    txt
cc                  email       copy    txt
bcc                 email       copy    txt
attach_name         email       copy    file name
attach_body type    attach_name copy    blob
grp         type    grp         email   name
*/

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    manage deferred emails

Notes
    (this is only a memo to not loose the knowledge)
    * the default job plan is 1h

Parameters
    @opt    options
            install     install the job
            list        list pending emails
            run         send pending emails now
            ena         enable the job
            dis         disable the job
            30mi        set plan of job every 30 minutes

Examples
    sp__email_job                       -- show this help

''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__email_job'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__email_job:

-- ===================================================================== sp__err
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__err',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__err') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__err') with nowait
        goto skip_sp__err
        end
    if @ver>131018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__err') with nowait
        goto skip_sp__err
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__err') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__err'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__err]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131018\s.zaglio: better help
    v:130927\s.zaglio: better output when called by exception
    v:130722.1000\s.zaglio: a small bug when under try catch
    v:130606\s.zaglio: a small bug when noerr opt
    v:121209\s.zaglio: changed exception msg format again(**)
    v:121206\s.zaglio: back to version 121002 because more correct call stack
    v:121121\s.zaglio: better help
    v:121118\s.zaglio: changed exception msg format and removed @lang,@tr
    v:121002\s.zaglio: a bug when called for exception
    v:120810\s.zaglio: warn option
    v:120809\s.zaglio: engine exception return
    v:120802\s.zaglio: better help
    v:120731\s.zaglio: added db name on print of @cod
    v:120223\s.zaglio: suppressed log of error
    v:111108\s.zaglio: added @opt and moved @src and and @trap
    v:110526\s.zaglio: refined #ex
    v:110523\s.zaglio: removed #err, added @src
    v:110329\s.zaglio: added print of exception error
    v:100706\s.zaglio: more help
    v:100228\s.zaglio: more help
    r:100223\s.zaglio: added params
    r:100222\s.zaglio: a review
    r:100127\s.zaglio: added more help and direct error raise and raise 20
    v:100117\s.zaglio: manage errors
    todo: test it remotelly
    t:sp__err ''immediate error %s'',''sp__this'',@trap=1
    t:sp__err ''immediate error %s'',''sp__this'',@p1=''test''    -- error(1794031170)
    t:sp__err_old
    t:sp__err_test
*/
CREATE proc [dbo].[sp__err]
    @msg    nvarchar(4000)=null out,
    @cod    sysname=null,
    @p1     sql_variant=null,
    @p2     sql_variant=null,
    @p3     sql_variant=null,
    @p4     sql_variant=null,
    @opt    sysname=null
as
begin
set nocount on
declare @proc sysname,@ret int
select
    @proc=object_name(@@procid),
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

declare
    @crlf nchar(2),@err bit,
    @type smallint,@level int,@n int,
    @trap bit,@src bit,@noerr bit,@ex bit,@warn bit,
    @e_msg nvarchar(4000),
    @db sysname

select
    @db=db_name(),
    @trap=charindex(''|trap|'',@opt),
    @src=charindex(''|src|'',@opt),
    @noerr=charindex(''|noerr|'',@opt),
    @ex=charindex(''|ex|'',@opt)
       |case @msg when ''#ex'' then 1 else 0 end,
    @warn=charindex(''|warn|'',@opt),
    @crlf=crlf,
    @e_msg=''error(%d) "%s" in [%s:%s]'',
    @ret=dbo.fn__crc32(@msg)
from dbo.fn__sym()

select
    @msg=dbo.fn__printf(@msg,@p1,@p2,@p3,@p4,null,null,null,null,null,null)

if @noerr=1 goto ret
if @trap=1 select @level=20 else select @level=16

if (@msg is null and @cod is null and @opt=''||'') goto help

-- select @msg=isnull(@msg,'''')

if @ex=1   -- used in a try catch from mssql2k5
    begin
    -- for compatibiliy with ms2k
    declare @err_message nvarchar(4000);
    declare @err_procedure nvarchar(4000);
    declare @err_severity int;
    declare @err_state int;
    declare @err_number int;
    declare @err_line nvarchar(8);

    select
        @ret=dbo.fn__crc32(''exception''),
        @err_message = isnull(@msg,     -- when redefined by developer
                              isnull(error_message(),''-- msg not specified --'')
                             ),
        @err_severity = case @warn when 1 then 10 else error_severity() end,
        @err_state = error_state(),
        @err_number = error_number(),
        @err_line = error_line(),
        @err_procedure = @db+''.''+isnull(error_procedure(),@cod)
                                     -- not correct when managed

    -- dynamics
    -- sp__err/raiserror -> exception -> exception
    -- error(nnn)        -> calledby  -> calledby

    if @err_message like ''error(%)%[[]%]%''
        select @e_msg=@err_message
    else
        select @e_msg=dbo.fn__printf(
                @e_msg,@ret,@err_message,@err_procedure,@err_line,
                null,null,null,null,null,null)
    if @cod!='''' and @cod!=error_procedure()
        select @e_msg=@e_msg
                     +@crlf
                     +''called by [''+@db+''.''+@cod+'':?]''

    select @e_msg=replace(@e_msg,''%'',''%%'')
    raiserror(@e_msg,@err_severity,@err_state) with nowait
    goto ret
    end -- print of exception error

-- sp__usage ''fn__printf''
if not @cod is null select @cod=@db+''.''+@cod

-- raiserror(''test %s'',10,1)
if @src=1 insert #src(line)
    select dbo.fn__printf(@e_msg,
                          @ret,@msg,@cod,null,
                          null,null,null,null,null,null)


select @msg=isnull(@msg,''''),@cod=isnull(@cod,'''')
raiserror(@e_msg,@level,1,@ret,@msg,@cod,''?'') with nowait

goto ret

help:
exec sp__usage @proc,''
Scope
    Introduce a common error management.

Notes
    raiserror <20 do not break SP execution in VS
    raiserror >10 break execution of SP if execute from step of job

Parameters:
    return  return the crc32 calculated on @msg (without replaces)
            and is the number reporter into ()
            (**) unfortunatelly with try/catch and the use or raiserror,
            we lost msg format so "2143915629" is the crc32 of "exception";
            when old style error management and new exception are mixed,
            the error report can contain stack calls ("called by [...]")
    @msg    the message with macros %s, %d or ''''#ex''''
    @proc   name of caller
    @p1..4  replaces %s, %d into @msg
    @opt    options
            trap    0(default) or 1, break the connection (raiserror >=20)
            src     if 1, store the message into #src before raise
                    #src(lno int identity,line nvarchar(4000))
            ex      print exception info, used into catch in mssql2k5 i.e.
                        begin catch
                            if error_number()!=1205 /*deadlock*/ exec sp__err @opt="ex"
                        end catch
                    message can be overridden:
                        exec sp__err "my msg",@proc,@opt="ex"
            noerr   calc on return code but do not raise error
            warn    replace severity with 10 to show only the error

Examples:

    exec @ret = sp__err ''''test''''

    produce

        Messagge 50000, level 16, state 1, procedure sp__err, row 122
        error(-662733300) "test" in [:?]

    the ? means unknown line number

    exec sp__err ''''test %s'''',''''sp__this'''',@p1=''''personal''''

        Messagge 50000, level 16, state 1, procedure sp__err, row 122
        error(-1628459270) "test personal" in [db.sp__this:line]"

    when caused by an exception, a message can be followed by:

        called by [proc:?]
        called by [proc:?]
        ...
''
select @ret=-1

ret:
return @ret
end -- sp__err'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__err:

-- ================================================================ sp__err_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__err_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130927
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__err_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__err_test') with nowait
        goto skip_sp__err_test
        end
    if @ver>130927
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__err_test') with nowait
        goto skip_sp__err_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__err_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__err_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__err_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:sp__err,test,old,style,new,style,try,catch
    r:130927\s.zaglio: test sp__err in various conditions
*/
CREATE proc sp__err_test
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
-- =========================================================== initialization ==
-- ======================================================== second params chk ==
-- =============================================================== #tbls init ==
-- ===================================================================== body ==

select @ret=dbo.fn__crc32(''exception'')
exec sp__printf ''-- "exception" error code:%d'',@ret

exec sp__printf ''-- 1) test new try-catch style with back compatibility''
begin try
raiserror(''this is the raised error (the line position is correct)'',16,1)
end try
begin catch
exec @ret=sp__err @cod=@proc,@opt=''ex''
exec sp__printf ''returned code:%d'',@ret
end catch

exec sp__printf ''-- 2) test new try-catch style with redefined message''
begin try
raiserror(''this is the error'',16,1)
end try
begin catch
exec @ret=sp__err ''redefined message with %d param'',@proc,@p1=''one'',@opt=''ex''
exec sp__printf ''returned code:%d'',@ret
end catch

exec sp__printf ''-- 3) test new try-catch style with "error" strip''
begin try
select @err_msg=''redefined message with %d param''
exec @ret=sp__err @err_msg out,@proc,@p1=''A'',@opt=''noerr''
exec sp__printf ''raising:%s'',@err_msg
raiserror(@err_msg,16,1)
end try
begin catch
exec @ret=sp__err @cod=@proc,@opt=''ex''
exec sp__printf ''returned code:%d'',@ret
end catch

exec sp__printf ''-- 4) inner try''
begin try
    begin try
    select @err_msg=''redefined message with %d param''
    exec @ret=sp__err @err_msg out,@proc,@p1=''A'',@opt=''noerr''
    exec sp__printf ''raising:%s'',@err_msg
    raiserror(@err_msg,16,1)
    end try
    begin catch
    exec @ret=sp__err @cod=@proc,@opt=''ex''
    exec sp__printf ''returned code:%d'',@ret
    end catch
end try
begin catch
end catch

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    [write here a short desc]

Parameters
    [param]     [desc]
    @opt        options
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end -- proc sp__err_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__err_test:

-- ==================================================================== sp__exec
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__exec',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130308
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__exec') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__exec') with nowait
        goto skip_sp__exec
        end
    if @ver>130308
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__exec') with nowait
        goto skip_sp__exec
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__exec') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__exec'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__exec]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130308\s.zaglio: better help and info
    v:121202\s.zaglio: better help; added dangerous like check; more options
    v:120611\s.zaglio: added %db% macro
    v:120523\s.zaglio: added @opt and run because sp too dangerouse
    v:101202\s.zaglio: added #objs test
    v:100919.1005\s.zaglio: added fast where
    v:100919\s.zaglio: added use of external #objs
    v:100723\s.zaglio: added %cols%
    v:100619\s.zaglio: added @fromdb,@p1..@p4
    v:100301\s.zaglio: execute a script for each object
    c:replaces old utilities sp__foreach??? into this one
    t:sp__exec ''print "%tbl%"'',@opt=''run'',@dbg=1
    t:sp__exec ''print "[%tbl%].[%tbl_col%]"'',@opt=''run'',@dbg=1
    t:sp__exec ''print "%syn%:%syn_base%"'',@opt=''run'',@dbg=1
    t:sp__exec ''print "%isql%"'',@where=''charindex("_str_","%obj%")>0'',@isql=''print "%obj%(FN,TF)"''
    t:sp__exec ''print "%tbl%:%cols%"'',@opt=''run'',@dbg=1
    t:sp__exec ''print "%db%"'',@opt=''run''
*/
CREATE proc sp__exec
    @sql nvarchar(4000)=null,
    @where nvarchar(4000)=null,
    @isql nvarchar(4000)=null,
    @fromdb sysname=null,
    @p1 sysname=null,
    @p2 sysname=null,
    @p3 sysname=null,
    @p4 sysname=null,
    @opt sysname=null,
    @dbg int=0
as
begin
set nocount on
declare @proc sysname,@ret int
select  @proc=object_name(@@procid),@ret=0,
        @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
if @sql is null goto help

declare @run bit,@db sysname,@nfo bit,@i int,@j int,@wrn bit

select
    @run=charindex(''|run|'',@opt),
    @nfo=charindex(''|nfo|'',@opt),
    @wrn=charindex(''|wrn|'',@opt),
    @i=patindex(''% like %["''''][%][[]%'',@where),
    @j=patindex(''%][%]["'''']%'',@where),
    @db=db_name()

if @i>0 and @j>@i and @wrn=0 goto err_like

if @where=''#objs''
    begin
    if object_id(''tempdb..#objs'') is null goto err_objs
    select @where=''"%tbl%" in (select obj from #objs)''
    end

if left(@where,6)=''%tbl%=''
    begin
    if charindex('',%col%='',@where)>0
        select @where=''"%tbl%" like "''+substring(dbo.fn__str_at(@where,'','',1),7,4000)+''" and ''
                     +''"%col%" like "''+substring(dbo.fn__str_at(@where,'','',2),7,4000)+''"''
    else
        select @where=''"%tbl%" like "''+substring(@where,7,4000)+''"''
    end

-- declare @sql sysname select @sql=''print "%obj%(u,v)"''
create table #objs_sp__exec(
    id int identity primary key,
    obj sysname,typ nvarchar(2),val sysname null,
    cols nvarchar(4000) null
    )
create table #syns (id int primary key,base sysname)   -- for compatibility with mssql2k
create table #typs (xtype nvarchar(2))
declare @col bit,@tsql nvarchar(4000)
declare @qfromdb sysname,@cols nvarchar(4000),@with_cols bit
select @col=0,@with_cols=0

select @fromdb=coalesce(dbo.fn__sql_unquotename(@fromdb),db_name())
select @qfromdb=dbo.fn__sql_quotename(@fromdb)

if dbo.fn__ismssql2k()=0
    insert #syns
    select [object_id],base_object_name
    from sys.synonyms

declare @prm table (id int,p nvarchar(4000) null)
insert @prm select 1,@sql
insert @prm select 2,@where
insert @prm select 3,@isql

if charindex(''%cols%'',@sql)>0
or charindex(''%cols%'',@isql)>0
    select @with_cols=1

-- check syntax
if exists(select null from @prm
          where charindex(''%obj%'',p)>0)
and not exists(select null from @prm
          where charindex(''%obj%('',p)>0)
    goto err_syn

select
    @tsql=
        substring(
            p,
            charindex(''%obj%('',p)+6,
            charindex('')'',p,charindex(''%obj%('',p))-charindex(''%obj%('',p)-6
            )
from @prm
where charindex(''%obj%('',p)>0

if not @tsql is null
    begin
    insert #typs
    select left(token,2)
    from dbo.fn__str_table(@tsql,'','')
    end

-- remove (t,t,t,...)
update @prm set
    p=left(p,charindex(''%obj%('',p)+4)
     +substring(p,charindex('')'',p,charindex(''%obj%('',p))+1,4000)
where charindex(''%obj%('',p)>0

-- reload adjusted params
select @sql=p   from @prm where id=1
select @where=p from @prm where id=2
select @isql=p  from @prm where id=3


if exists(select null from @prm where charindex(''%tbl%'',p)>0)
    insert #typs select ''U''
if exists(select null from @prm where charindex(''%view%'',p)>0)
    insert #typs select ''V''
if exists(select null from @prm where charindex(''%syn%'',p)>0)
    insert #typs select ''SN''
if exists(select null from @prm
          where charindex(''%col%'',p)>0
          or charindex(''%view_col%'',p)>0
          or charindex(''%tbl_col%'',p)>0
         )
    begin
    select @col=1
    if charindex(''%col%'',@sql)>0
    or charindex(''%col%'',@isql)>0
        insert #typs select ''U'' union select ''V''
    if charindex(''%tbl_col%'',@sql)>0
    or charindex(''%tbl_col%'',@isql)>0
        insert #typs select ''U''
    if charindex(''%view_col%'',@sql)>0
    or charindex(''%view_col%'',@isql)>0
        insert #typs select ''V''

    select @tsql=''
    insert #objs_sp__exec(obj,typ,val,cols)
    select o.[name],o.xtype,c.name,''+
        case when @with_cols=1
             then ''dbo.fn__flds_of(o.[name],'''','''',null)''
             else ''null''
        end+''
    from ''+@qfromdb+''..sysobjects o
    join ''+@qfromdb+''..syscolumns c on c.id=o.id
    left join #syns s on o.id=s.id and o.xtype=''''SN''''
    where o.xtype in (select xtype from #typs)
    order by o.xtype,c.colorder
    ''
    exec(@tsql)

    end -- insert cols
else
    begin
    select @tsql=''
    insert #objs_sp__exec(obj,typ,val,cols)
    select o.[name],o.xtype,s.base,''+
        case when @with_cols=1
             then ''dbo.fn__flds_of(o.[name],'''','''',null)''
             else ''null''
        end+''
    from ''+@qfromdb+''..sysobjects o
    left join #syns s on o.id=s.id and o.xtype=''''SN''''
    where o.xtype in (select xtype from #typs)
    order by o.xtype
    ''
    exec(@tsql)
    end

-- if @dbg=1 exec sp__select_astext ''select * from #objs_sp__exec''

select @sql=replace(@sql,''"'','''''''')
select @where=replace(@where,''"'','''''''')
select @isql=replace(@isql,''"'','''''''')

declare @tkns nvarchar(4000)
select @tkns=''%tbl%|%view%|%syn%|%tbl_col%|%view_col%|%syn_base%|%base%|%col%''

exec sp__str_replace
        @sql out,
        @tkns,
        ''%obj%'',''%obj%'',''%obj%'',''%val%'',''%val%'',''%val%'',''%val%'',''%val%''
if not @isql is null
    exec sp__str_replace
            @isql out,
            @tkns,
            ''%obj%'',''%obj%'',''%obj%'',''%val%'',''%val%'',''%val%'',''%val%'',''%val%''
if not @where is null
    exec sp__str_replace
            @where out,
            @tkns,
            ''%obj%'',''%obj%'',''%obj%'',''%val%'',''%val%'',''%val%'',''%val%'',''%val%''

if not exists(select null from #objs_sp__exec)
and (charindex(''%db%'',@sql)>0 or charindex(''%fromdb%'',@sql)>0)
    insert #objs_sp__exec(obj) select @fromdb

declare @b bit,@obj sysname,@xt nvarchar(2),@val sysname,@n int
select @n=0
declare cs cursor local for
    select obj,typ,val,cols
    from #objs_sp__exec
    order by id
open cs
while 1=1
    begin
    fetch next from cs into @obj,@xt,@val,@cols
    if @@fetch_status!=0 break

    -- if @dbg=1 exec sp__printf ''processing (%s,%s,%s) with (%s)'',@obj,@xt,@val,@sql

    select @b=1
    if not @where is null
        begin
        select @b=0
        select @tsql=''if (''+@where+'') select @b=1''
        exec sp__str_replace
                @tsql out,
                ''%obj%|%xt%|%val%|%fromdb%|%db|'',
                @obj,@xt,@val,@fromdb,@db
        exec sp_executesql @tsql,
            N''@b bit out,@obj sysname,@xt sysname,@val sysname'',
            @b=@b out,@obj=@obj,@xt=@xt,@val=@val
        if @@error!=0 exec sp__printf ''error in where:\n%s'',@tsql
        if @dbg=2 exec sp__printf ''b:%s; sql:%s; @val:%s;'',@b,@tsql,@val
        end

    if @b=1
        begin
        select @tsql=@sql
        if not @isql is null
            select @tsql=replace(@tsql,''%isql%'',replace(@isql,'''''''',''''''''''''))

        -- prepare final sql
        exec sp__str_replace
                @tsql out,
                ''%obj%|%xt%|%val%|%fromdb%|%cols%|%db%'',
                @obj,@xt,@val,@fromdb,@cols,@db

        if not @p1 is null select @tsql=replace(@tsql,''%p1%'',@p1)
        if not @p2 is null select @tsql=replace(@tsql,''%p2%'',@p2)
        if not @p3 is null select @tsql=replace(@tsql,''%p3%'',@p3)
        if not @p4 is null select @tsql=replace(@tsql,''%p4%'',@p4)

        select @n=@n+1

        if @dbg=1 or @run=0
            exec sp__printf ''%s'',@tsql
        else
            begin
            exec(@tsql)
            if @@error!=0 and @dbg=0 exec sp__printf ''-- error in:%s'',@tsql
            end
        end
    end -- loop objs

close cs
deallocate cs

if @n=0 exec sp__printf ''-- no objects match condition; use @dgb 2 to see''

goto ret

-- =================================================================== errors ==
err_syn:
    exec @ret=sp__err ''expected ( after %obj%'',@proc
    goto ret
err_objs:
    exec @ret=sp__err ''#objs not found; see help'',@proc
    goto ret
err_like:
    exec @ret=sp__err ''likes like "%[...]%" are not admitted (see wrn option)'',
                      @proc
    goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    execute script for each object of db

Parameters
    @sql    sql template to execute for each object identified
    @isql   is the inside sql used to replace macro %isql%
    @fromdb to load object name from db different from current
    @dbg    1 cmd will be printed and not executed even if RUN is specified
            2 print search sql
    @p1...  replace %p1%,... after macro
    @where  contain an expression that filter objects trought macros
            can be a fast expression like:
            "#objs"     : become: "%tbl% in (select obj from #objs)"
            %tbl%=...   : become: "%tbl%" like "..."
            Macros
                @obj    object name
                @xt     object type (U,V,...)
                @val    in case of synonyms, is the for clause
                        in other cases is the column name
            Notes
                likes like "%[...]%" are not admitted

Options
    run     since 120523, this sp show code only because is to dangerouse.
            Use this option to run it.
    wrn     skip error of dangerouse like

Macros
    %obj%(t,...)is a replacer for each object of specified type
    %tbl%       is a replacer for tables
    %view%      is a replacer for views
    %syn%       is a replacer for synonym
    %syn_base%  is a replacer for synonym reference
    %col%       is a replacer for columns of view or table
    %cols%      is a replacer for "col1,col2,..." of view or table
    %tbl_col%   is a replacer for [table].[column]
    %view_col%  is a replacer for [table].[column]
    %typ%       is a replacer for type(size)    (TODO)
    %master%    is a replacer for master server name in sync system (TODO)
    %master%    is a replacer for slave  server name in sync system (TODO)
    %isql%      is a replacer for inner sql (param @isql)
    %fromdb%    is a replacer from @fromdb
    %db%        is a replacer for db_name()
    "           is a replacer for double quotes

Each macro enable collection of relative object

Examples
    sp__exec ''''print "%tbl%"'''',@dbg=1
    sp__exec ''''print "[%tbl%].[%tbl_col%]"'''',@dbg=1
    sp__exec ''''print "%sym%:%sym_base%"'''',@dbg=1
    sp__exec ''''print "%isql%"'''',@where=''''charindex("_str_","%obj%")>0'''',@isql=''''print "%obj%(FN,TF)"'''',@dbg=1

Objects types
    AF = funzione di aggregazione (CLR)
    C = vincolo CHECK
    D = DEFAULT (vincolo o valore autonomo)
    F = vincolo FOREIGN KEY
    PK = vincolo PRIMARY KEY
    P = stored procedure SQL
    PC = stored procedure assembly (CLR)
    FN = funzione scalare SQL
    FS = funzione scalare assembly (CLR)
    FT = funzione valutata a livello di tabella assembly (CLR)
    R = regola (tipo obsoleto, autonoma)
    RF = procedura-filtro-replica
    S = tabella di base di sistema
    SN = sinonimo
    SQ = coda di servizio
    TA = trigger DML assembly (CLR)
    TR = trigger DML SQL
    IF = funzione SQL inline valutata a livello di tabella
    TF = funzione valutata a livello di tabella SQL
    U = tabella (definita dall''''utente)
    UQ = vincolo UNIQUE
    V = vista
    X = stored procedure estesa
    IT = tabella interna

''

-- ===================================================================== exit ==
ret:
return @ret
end -- proc sp__exec'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__exec:

-- ============================================================= sp__execute_DTS
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__execute_DTS',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__execute_DTS') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__execute_DTS') with nowait
        goto skip_sp__execute_DTS
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__execute_DTS') with nowait
        goto skip_sp__execute_DTS
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__execute_DTS') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__execute_DTS'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__execute_DTS]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091018\s.zaglio: run dts from code
*/
CREATE   PROC sp__execute_DTS
    @PkgName nvarchar(255),             -- Package Name (Defaults to most recent version)
    @param1 sysname=null,
    @value1 sysname=null,
    @Server nvarchar(255)=null,
    @ServerPWD nvarchar(255) = Null,        -- Server Password if using SQL Security to load Package (UID is SUSER_NAME())
    @IntSecurity bit = 0,            -- 0 = SQL Server Security, 1 = Integrated Security
    @PkgPWD nvarchar(255) = ''''        -- Package Password
AS

/* examples:
    -- for running SQL server security authorization
    spExecuteDTS ''127.0.0.1'', ''dtsImportData'', ''sa''
    --/ or /--
    spExecuteDTS ''127.0.0.1'', ''dtsImportData'', ''sa'', 0

    -- for running SQL server security authorization
    spExecuteDTS ''127.0.0.1'', ''dtsImportData'', ''sa'', 1
*/

SET NOCOUNT ON
/*
    Return Values
    - 0 Successfull execution of Package
    - 1 OLE Error
    - 9 Failure of Package
*/

if @Server is null set @Server=@@servername

DECLARE @hr int, @ret int, @oPKG int, @Cmd nvarchar(1000)

-- Create a Pkg Object
EXEC @hr = sp_OACreate ''DTS.Package'', @oPKG OUTPUT
IF @hr <> 0
BEGIN
    PRINT ''***  Create Package object failed''
    EXEC sp__displayoaerrorinfo @oPKG, @hr
    RETURN 1
END

-- Evaluate Security and Build LoadFromSQLServer Statement
IF @IntSecurity = 0
    SET @Cmd = ''LoadFromSQLServer("'' + @Server +''", "'' + SUSER_SNAME() + ''", "'' + @ServerPWD + ''", 0, "'' + @PkgPWD + ''", , , "'' + @PkgName + ''")''
ELSE
    SET @Cmd = ''LoadFromSQLServer("'' + @Server +''", "", "", 256, "'' + @PkgPWD + ''", , , "'' + @PkgName + ''")''

EXEC @hr = sp_OAMethod @oPKG, @Cmd, NULL

IF @hr <> 0
BEGIN
        PRINT ''***  LoadFromSQLServer failed''
        EXEC sp__displayoaerrorinfo @oPKG , @hr
        RETURN 1
END

-- set parameters
if not @param1 is null begin
    EXEC @hr = sp_OAMethod @oPKG, ''AddGlobalVariable'', @Name = @param1, @Value = @value1
    IF @hr <> 0 PRINT ''Cant Add Global Var''
end




-- Execute Pkg
EXEC @hr = sp_OAMethod @oPKG, ''Execute''
IF @hr <> 0
BEGIN
        PRINT ''***  Execute failed''
        EXEC sp__displayoaerrorinfo @oPKG , @hr
        RETURN 1
END

-- Check Pkg Errors
EXEC @ret=sp__DisplayPkgErrors @oPKG

-- Unitialize the Pkg
EXEC @hr = sp_OAMethod @oPKG, ''UnInitialize''
IF @hr <> 0
BEGIN
        PRINT ''***  UnInitialize failed''
        EXEC sp__displayoaerrorinfo @oPKG , @hr
        RETURN 1
END

-- Clean Up
EXEC @hr = sp_OADestroy @oPKG
IF @hr <> 0
BEGIN
    EXEC sp__displayoaerrorinfo @oPKG , @hr
    RETURN 1
END

RETURN @ret'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__execute_DTS:

-- ================================================================== sp__exists
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__exists',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__exists') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__exists') with nowait
        goto skip_sp__exists
        end
    if @ver>100919
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__exists') with nowait
        goto skip_sp__exists
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__exists') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__exists'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__exists]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100919\s.zaglio: show help about fn__Exists and can be used to test remote server obj existance
*/
CREATE proc sp__exists
    @dbg bit=0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname,  @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=''sp__exists'', @ret=0

-- ========================================================= param formal chk ==
-- ============================================================== declaration ==
-- =========================================================== initialization ==
-- ======================================================== second params chk ==
-- ===================================================================== body ==
-- goto ret
-- =================================================================== errors ==
-- ===================================================================== help ==
help:
exec sp__usage ''fn__exists'',''
Scope
    show usage of fn__exists

Parameters
    @objects    name of obj or
            obj1,obj2,...   test if exists "obj1 and obj2 and ..."
            obj1|obj2|...   test if exists "obj1 or  obj2 or  ..."
                can verify existance of #temp objects
                sp__exists can test on remote servers (TODO)

    @type       can be null for any object or:
            fk  test if exists a foreign key for that object
            fl  meand that objX is a tbl.fld

    @dbg        run tests for all functionality (TODO)

Examples
    create table test_ex1(a int)
    print dbo.fn__exists(''''test_ex1'''',default)      -- 1
    print dbo.fn__exists(''''test_ex1.a'''',''''fl'''')     -- 1
    print dbo.fn__exists(''''test_ex1.notx'''',''''fl'''')  -- 0
    drop table test_ex1
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__exists'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__exists:

-- ================================================================ sp__file_get
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__file_get',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130906
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__file_get') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__file_get') with nowait
        goto skip_sp__file_get
        end
    if @ver>130906
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__file_get') with nowait
        goto skip_sp__file_get
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__file_get') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__file_get'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__file_get]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:get,file,web,ftp,drive,disk,storage,download
    r:130906\s.zaglio: adapting to new fn__parse_url and sp__parse_url
    r:130829\s.zaglio: common call for more specified sp to read/download a file
    t:sp__file_get ''%temp%\utility\index.txt''
*/
CREATE proc sp__file_get
    @uri nvarchar(2048) = null out,
    @var varbinary(max) = null out,
    @out nvarchar(1024) = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @text bit,
    @i int,@n int,                         -- index, counter
    @sql nvarchar(max),                    -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @protocol sysname,
    @uid sysname,
    @pwd sysname,
    @host sysname,
    @port int,
    @path nvarchar(4000),
    @page nvarchar(4000),
    @temp nvarchar(512),
    @end_declare bit

-- =========================================================== initialization ==
exec sp__get_temp_dir @temp out
select
    @uri=replace(@uri,''%temp%'',@temp),
    @text=charindex(''|text|'',@opt),
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if nullif(@uri,'''') is null goto help

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

-- sp__parse_url_test

select
    @protocol=protocol,
    @uid=uid,
    @pwd=pwd,
    @host=host,
    @path=path,
    @page=page
from fn__parse_url(@uri,default)

if @protocol=''ftp'' raiserror(''ftp not yet supported'',16,1)

if @protocol in (''http'',''https'')
    begin
    if @dbg>0 exec sp__printf ''-- getting via web''
    exec sp__web @uri,@rsp=@sql out
    -- select @sql=replace(@sql,@crlf+@crlf,@crlf)
    end

if @protocol=''file''
    begin
    if @dbg>0 exec sp__printf ''-- reading file %s'',@uri
    -- not the faster but read 2MB in 7 seconds on localmachine

    select @sql=''''
    if @text=1
        begin
        exec sp__file_read_stream @uri,@out=@sql out
        select @var=cast(@sql as varbinary(max))
        end
    else
        begin
        select @sql=''select @var=BulkColumn ''
                   +''from openrowset(bulk ''''''+@path+''\''+@page+'''''',single_clob)''
                   +'' as x''
        exec sp_executesql @sql,N''@var image out'',@var=@var out
        end

    select @n=len(@sql)
    if @dbg>0 exec sp__printf ''-- loaded %d chars'',@n
    end

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    get a file from disk, web or ftp (todo) and store into @var or into
    the file specified by @out

Notes
    if executed from console, show the result, useful for tests

Parameters
    [param]     [desc]
    @uri        uniform resource identifier (see sp__parse_url_test)
    @var        image
    @opt        options
                text    inform the sp that the file is a text file
                        default is a binary file
    @dbg        info level

Examples
    sp__file_get "%temp%\utility\index.txt"
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__file_get'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__file_get:

-- =============================================================== sp__file_read
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__file_read',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100127
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__file_read') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__file_read') with nowait
        goto skip_sp__file_read
        end
    if @ver>100127
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__file_read') with nowait
        goto skip_sp__file_read
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__file_read') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__file_read'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__file_read]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100127\s.zaglio:deprecated use of fn__inject and sp__readtable
    v:090928\s.zaglio:removed a double cmd execution and added remove of cr
    v:090731\S.Zaglio:added external temp table management
    v:090123\S.Zaglio:renamed old sp__readtextfile andreplaced in other sp
    v:081009\S.Zaglio:added @step
    v:080916\S.Zaglio:added @outputvar
    v:080815\s.zaglio:added @dbg as param and [] around outputable
    v:080414\s.zaglio:removed content variable
*/
CREATE proc [dbo].[sp__file_read]
    @textfilename sysname,
    @outputtable nvarchar(128) = null,
    @outputvar nvarchar(4000) =null out,
    @step int=1,
    @dbg bit=0
as
set nocount on
declare @tmp bit set @tmp=0
if @dbg=1 print ''sp__file_read''
if (@textfilename=''-?'') goto help
if @outputtable is null and @outputvar is null goto help

if @outputtable is null begin
    set @outputtable=''tmp_''+convert(nvarchar(64),newid())
    set @tmp=1
end

if left(@outputtable,1)<>''['' set @outputtable=''[''+@outputtable+'']''
declare @sql nvarchar(4000)
if left(@outputtable,2)<>''[#'' -- if no external temp table generated
    begin
    set @sql=''create table %outputtable% (lno int identity(%step%,%step%), line nvarchar(4000))''
    exec sp__str_replace @sql out,''%outputtable%|%step%'',@outputtable,@step
    if @dbg=1 print @sql
    exec (@sql)
    end

declare @cmd nvarchar(4000), @crlf nchar(2)
set @cmd=''type "''+@textfilename+''"''
set @crlf=char(13)+char(10)
if @dbg=1 print @cmd
-- exec master.dbo.xp_cmdshell @cmd,no_output

set @sql=''insert ''+@outputtable+'' (line)''
set @sql=@sql+'' exec master.dbo.xp_cmdshell ''''''+replace(@cmd,'''''''','''''''''''')+''''''''
if @dbg=1 print @sql
exec(@sql)

if not @outputvar is null
    exec sp__err ''sp__readtable deprecated'',@cod=''sp__file_read''
-- exec sp__readtable @outputtable,@outputvar out,@dbg=@dbg
if @tmp=1 begin
    set @sql=''drop table ''+@outputtable
    exec(@sql)
end
return 0

help:
/*
exec sp_usage @objectname=''sp_readtextfile'',
@desc=''reads the contents of a ntext file into a sql result set'',
@parameters=''@textfilename=name of file to read, @contents=optional output var
to receive contents of file (up to 8000 bytes)'',
@author=''ken henderson'', @email=''khen@khen.com'',
@version=''8'',@revision=''0'',
@datecreated=''19960501'', @datelastchanged=''20000120'',
@example=''sp_readtextfile ''''d:\mssql7\log\errorlog'''' ''
*/
return -1'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__file_read:

-- ========================================================== sp__file_read_blob
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__file_read_blob',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131017
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__file_read_blob') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__file_read_blob') with nowait
        goto skip_sp__file_read_blob
        end
    if @ver>131017
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__file_read_blob') with nowait
        goto skip_sp__file_read_blob
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__file_read_blob') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__file_read_blob'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__file_read_blob]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131017\s.zaglio: about help
    v:121021\s.zaglio: added @opt and option asm with help
    v:100915.1559\s.zaglio: added more help
    v:100228\s.zaglio: load binary file into temp table
    v:100104\s.zaglio: load a binary file a print inserts
    s:sp__write_ntext_to_lines for text blob
    c:originally from www.sql.ru (mythical boys)
    t:
        declare @path varchar(1024),@cmd sysname
        select @path=''c:\windows\system32\findstr.exe''
        select @cmd=''dir "''+@path+''"''
        exec sp__file_read_blob @path,''%temp%\test.bin'',@uid=''uid'',@pwd=''pwd''
    t:
        exec sp__run_cmd ''dir "%SystemRoot%\system32\cmd.exe"'' -- 389120
        create table #blob (blob image)
        exec sp__file_read_blob ''c:\windows\system32\cmd.exe'',@out=''#blob.blob'',@dbg=1
        select datalength(blob) from #blob
        drop table #blob
    t:exec sp__file_read_blob ''c:\windows\system32\cmd.exe'',@opt=''asm''

*/
CREATE proc [dbo].[sp__file_read_blob]
    @path sysname=null,
    @out sysname=null,
    @uid sysname=''null'',
    @pwd sysname=''null'',
    @opt sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare
    @proc sysname,@tmp sysname,@ret int,
    @buffer varbinary (8000),
    @msg nvarchar(4000),
    @adodbstream int,
    @hr int,@size int,@ssize nvarchar(10),
    @file sysname,@cmd sysname,
    @ptr varbinary(64),
    @asm bit,@n int,
    @end_declare bit

select
    @proc=object_name(@@procid),@ret=0,
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @path like ''%..%'' goto err_wpro

if @path is null goto help

select @tmp=''##blob'' --''[tmp''+replace(convert(sysname,newid()),''-'','''')+'']''

if charindex(''\'',@out)>0 select @file=@out,@out=null
select @asm=charindex(''|asm|'',@opt)

if @out is null select @size=64
else select @size=8000

if @dbg=1 exec sp__printf ''init stream...''

select @ssize=convert(nvarchar(10),@size)
select @cmd=''adodb.stream''
exec @hr = sp_oacreate @cmd, @adodbstream out
if @hr!=0 goto err
select @cmd=''type''
exec @hr = sp_oasetproperty  @adodbstream ,@cmd,1
if @hr!=0 goto err
select @cmd=''open''
exec @hr = sp_oamethod  @adodbstream , @cmd, null
if @hr!=0 goto err
select @cmd=''loadfromfile''
exec @hr = sp_oamethod  @adodbstream , @cmd, null, @path
if @hr!=0 goto err
select @cmd=''read''
exec @hr = sp_oamethod  @adodbstream , @cmd, @buffer out, @ssize
if @hr!=0 goto err

if @dbg=1 exec sp__printf ''read chunks...''

if @asm=0
    begin
    if @out is null
        begin
        print ''declare @ptr varbinary(64)''
        print ''create table ''+@tmp+''(blob image)''
        print ''insert ''+@tmp+'' values(''+dbo.fn__hex(convert(varbinary(64),substring(@buffer,1,@size)))+'')''
        print ''select @ptr=textptr(blob) from ''+@tmp
        end
    else
        begin
        if object_id(''tempdb..#blob'') is null goto err_tbl
        insert #blob values (@buffer)
        select @ptr=textptr(blob) from #blob
        end
    end -- !asm

select @n=0
while @buffer is not null
    begin
    select @cmd=''read''
    exec @hr = sp_oamethod  @adodbstream , @cmd, @buffer out, @ssize out

    if @hr!=0 goto err
    -- print dbo.fn__hex(@ptr)
    if @asm=1
        begin
        if @n=0
            select @msg=dbo.fn__hex(convert(varbinary(64),
                                    substring(@buffer,1,@size)))+''\''
        else
            select @msg=substring(dbo.fn__hex(convert(varbinary(64),
                                              substring(@buffer,1,@size))),
                                  3,@size*2)+''\''
        exec sp__printf ''%s'',@msg
        end
    else
        begin
        if @out is null
            begin
            select @msg=dbo.fn__hex(convert(varbinary(64),
                                    substring(@buffer,1,@size)))+''\''
            print ''updatetext ''+@tmp+''.blob @ptr null 0 ''+@msg
            end
        else
            begin
            updatetext #blob.blob @ptr null 0 @buffer
            end
        end -- !asm
    select @n=@n+1
    end --loop

if @dbg=1 select datalength(blob) as dl, blob  as fdata from #blob

if @asm=0
    begin
    if @out is null
        begin
        if not @file is null
            begin
            select @msg =''exec sp__file_write_blob ''''select top 1 blob from %s'''',''+
                        +''''''%s'''',@uid=%s,@pwd=%s''
            exec sp__printf @msg,@tmp,@file,@uid,@pwd
            select @msg=''exec sp__run_cmd ''''fc /b "%s" "%s"''''''
            exec sp__printf @msg,@path,@file
            select @msg=''exec sp__run_cmd ''''dir "%s" & dir "%s"''''''
            exec sp__printf @msg,@path,@file
            select @msg=null
            end
        print ''drop table ''+@tmp
        end
    end -- !asm

goto ret

-- =================================================================== errors ==
err_wpro:   exec @ret=sp__err ''worked protection against hackers :-) '',@proc
            goto ret
err_tbl:    exec @ret=sp__err ''caller must create table #blob(blob image)'',@proc
            goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,N''
Scope
    Load a file into a blob or script it.

Notes
    maybe today is better use one of:
    -- varbinary
    select BulkColumn from openrowset(bulk "%path%", single_blob) as blob
    -- varchar (until 2k12, does not support code page 65001(UTF-8 encoding))
    select BulkColumn from openrowset(bulk "%path%", single_clob) as blob
    -- nvarchar (require BOM)
    select BulkColumn from openrowset(bulk "%path%", single_nclob) as blob

See also
    * sp__file_read_stream

Parameters
    @path   source file
    @out    destination table.blob, file or null
            if null print the script to write
            else with sp__file_write_blob
    @uid    optional when @out is a file
    @pwd    optional when @out is a file
    @opt    options
            asm     script only hex code
    @dbg    1:debug info


Eample:
    create table #blob (blob image)
    exec sp__file_read_blob ''''file_path'''',@out=''''#blob.blob''''
    insert into #mytable(image_fld) select blob from #blob
    ...
''
select @ret=-1
goto ret

err:
declare @source nvarchar(255)
declare @description nvarchar(255)

exec @hr = sp_oageterrorinfo @adodbstream, @source out, @description out
exec @hr = sp_oadestroy @adodbstream
select @adodbstream=null
exec @ret=sp__err ''ole error (%s;%s;%s)'',@proc,@p1=@cmd,@p2=@source,@p3=@description
goto ret

-- ===================================================================== exit ==
ret:
if @adodbstream!=0 exec @hr = sp_oadestroy @adodbstream
set nocount off
return @ret
end -- [sp__file_read_blob]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__file_read_blob:

-- ======================================================== sp__file_read_stream
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__file_read_stream',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130612
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__file_read_stream') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__file_read_stream') with nowait
        goto skip_sp__file_read_stream
        end
    if @ver>130612
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__file_read_stream') with nowait
        goto skip_sp__file_read_stream
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__file_read_stream') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__file_read_stream'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__file_read_stream]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130612\s.zaglio: @out to null if error
    v:130529\s.zaglio: added out to @out
    v:120406\s.zaglio: replaced #blob with #spfrsblob
    v:110921\s.zaglio: adapted to use of fn__ntext_to_lines
    v:110316\s.zaglio: adapted to new sp__write_ntext_to_lines #blob struct.
    v:100915\s.zaglio: added more help
    v:100603\s.zaglio: caos about line end
    v:100514.1436\s.zaglio: load txt file with different format (utf8, etc.)
    c:
        the filesystem object don''t read utf8 txt files
        the type of cmd do not read utf8
        BULK INSERT #blob FROM ''file.txt'' do not read utf8
    t:
        exec xp_cmdshell ''echo line 1 >%temp%\test.txt''
        exec xp_cmdshell ''echo line 2 >>%temp%\test.txt''
        create table #src(lno int identity,line nvarchar(4000))
        exec sp__file_read_stream ''%temp%\test.txt'',@out=''#src'',@dbg=1
        select * from #src order by lno
        drop table #src
*/
CREATE proc [dbo].[sp__file_read_stream]
    @path nvarchar(512)=null,
    @out nvarchar(max)=null out,
    @fmt sysname=''utf-8'',
    @ls int=-1,
    @uid sysname=''null'',
    @pwd sysname=''null'',
    @dbg bit=0
as
begin
set nocount on
declare
    @proc sysname,@tmp sysname,@ret int,
    @msg nvarchar(4000),--@buffer varchar(4000),
    @adodbstream int,
    @hr int,@size int,@ssize nvarchar(10),
    @chunk nvarchar(4000),
    @file sysname,@cmd sysname,
    @ptr varbinary(64),@n int,
    @end_declare bit

create table #spfrsblob(id int identity,blob ntext null)
insert #spfrsblob(blob) select ''''
select top 1 @ptr=textptr(blob) from #spfrsblob where id=1
if @ptr is null goto err_ptr
-- drop table #buffer

select @proc=object_name(@@procid),@ret=0
if @path is null goto help

if @path like ''%[%]temp[%]%''
    begin
    exec sp__get_temp_dir @tmp out
    select @path=replace(@path,''%temp%'',@tmp)
    if @dbg=1 exec sp__printf ''path:%s'',@path
    end

if @path like ''%..%'' goto err_wpro

select @size=4000

if @dbg=1 exec sp__printf ''init stream...''

select @ssize=convert(nvarchar(10),@size)
select @cmd=''ADODB.Stream''
exec @hr = sp_oacreate @cmd, @adodbstream out
if @hr!=0 goto err
select @cmd=''Type''
exec @hr = sp_oasetproperty  @adodbstream ,@cmd,2 -- text
if @hr!=0 goto err
select @cmd=''charset''
exec @hr = sp_oasetproperty  @adodbstream ,@cmd,@fmt
if @hr!=0 goto err
select @cmd=''LineSeparator''
exec @hr = sp_oasetproperty  @adodbstream ,@cmd,@ls
if @hr!=0 goto err
select @cmd=''Open''
exec @hr = sp_oamethod  @adodbstream , @cmd, null
if @hr!=0 goto err
select @cmd=''LoadFromFile''
exec @hr = sp_oamethod  @adodbstream , @cmd, null, @path
if @hr!=0 goto err
select @cmd=''ReadText''

while (1=1)
    begin
    select @chunk=null
    exec @hr = sp_oamethod  @adodbstream , @cmd, @chunk out ,@size  -- with @size=.2 means read line
    select @n=len(@chunk)
    if @dbg=1 exec sp__printf ''chunk of %d:%s'',@n,@chunk
    if @hr!=0 goto err
    if @n=0 or @chunk is null break
    updatetext #spfrsblob.blob @ptr null 0 @chunk
    end

if @out is null
    -- exec sp__write_ntext_to_lines @dbg=@dbg
    select line
    from dbo.fn__ntext_to_lines((select top 1 blob from #spfrsblob),0)
    order by lno
else
    begin
    if @out=''#src''
        insert #src(line) select line
        from dbo.fn__ntext_to_lines((select top 1 blob from #spfrsblob),0)
        order by lno
    if @out=''#out''
        insert #out(line) select line
        from dbo.fn__ntext_to_lines((select top 1 blob from #spfrsblob),0)
        order by lno
    if @out=''''
        select top 1 @out=blob from #spfrsblob
    end -- out to know tables

-- drop table #blob

goto ret

help:
exec sp__usage @proc,''
Scope
    Import a text file

Parameters
    @path   source file path
    @out    can be #src or #out or nothing to out as recordset
            if is an empty string '''''''', out to @out itself
    @fmt    is the format of source file; by default is "utf-8"
            Other format are gived from constants acecpted by
            adodb.stream "charset" property.
    @ls     line separator (default -1 for CRLF else 10 for LF or 13 for CR)
            Unfortunatelly, MSSQL generates log that are incompatibile with
            this (or after too many tests I have not found one good)
            In that case the old xp_cmdshell "type ..." work well.

Notes
    BULK INSERT #blob FROM ''''file.txt'''' do not read utf8

See also
    * http://www.w3schools.com/ado/ado_ref_stream.asp
    * sp__write_ntext_to_lines
    * in MS2K5 can use
        select BulkColumn
        from openrowset(bulk ''''file'''',  single_clob) as x
            single_clob     text
            single_nclob    ntext   (not support utf8 and require specific unicode txt file)
            single_blob     image
    * ms-help://MS.SQLCC.v9/MS.SQLSVR.v9.it/tsqlref9/html/f47eda43-33aa-454d-840a-bb15a031ca17.htm

Example
    exec xp_cmdshell ''''echo line 1 >%temp%\test.txt''''
    exec xp_cmdshell ''''echo line 2 >>%temp%\test.txt''''
    create table #src(lno int identity,line nvarchar(4000))
    exec sp__file_read_stream ''''%temp%\test.txt'''',@out=''''#src'''',@dbg=1
    select * from #src order by lno
    drop table #src
''
select @ret=-1
goto ret

err:
declare @source nvarchar(255)
declare @description nvarchar(255)

exec @hr = sp_oageterrorinfo @adodbstream, @source out, @description out
exec @hr = sp_oadestroy @adodbstream
select @adodbstream=null,@out=null
exec @ret=sp__err ''ole error (%s;%s;%s)'',@proc,@p1=@cmd,@p2=@source,@p3=@description
goto ret

err_wpro:   exec @ret=sp__err ''worked protection against hackers :-) '',@proc goto ret
err_ptr:    exec @ret=sp__err ''ptr get error'',@proc goto ret

ret:
if @adodbstream!=0 exec @hr = sp_oadestroy @adodbstream
set nocount off
return @ret
end -- [sp__file_read_stream]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__file_read_stream:

-- ============================================================== sp__file_write
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__file_write',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110523
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__file_write') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__file_write') with nowait
        goto skip_sp__file_write
        end
    if @ver>110523
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__file_write') with nowait
        goto skip_sp__file_write
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__file_write') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__file_write'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__file_write]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110523\s.zaglio: better debug and partial remake
    v:110415\s.zaglio: better debug
    v:100405\s.zaglio: added proc to sp__err
    v:100402\s.zaglio: removed out to xls on help
    v:100311\s.zaglio: deprecated output to xls,cvs because bcp out txt with xls extension and not binary xls
    v:100228\s.zaglio: more specific help and out to excel if file and with .xls
    v:090928\s.zaglio: added @unicode
    v:090925\s.zaglio: some auto corrections
    v:090123\s.zaglio: rewrited mixing from
    http://www.simple-talk.com/sql/t-sql-programming/reading-and-writing-files-in-sql-server-using-t-sql/
    v:080710/s.zaglio: write a table or a multi-string to a file (replace old sp__writetextfile)
    t:
        -- simple test
        declare @st sysname set @st=''this is a test''+char(13)+char(10)+''hello line 2''
        exec sp__file_write ''%temp%\test.txt'',@text=@st
        exec sp__run_cmd ''type %temp%\test.txt'',@dbg=1

        -- simple test with append
        exec sp__file_write ''%temp%\test.txt'',@text=''opened for append'',@addcrlf=1
        exec sp__file_write ''%temp%\test.txt'',@text=''appended 1'',@append=1,@addcrlf=1
        exec sp__file_write ''%temp%\test.txt'',@text=''appended 2'',@append=1,@addcrlf=1
        exec sp__run_cmd ''type %temp%\test.txt'',@dbg=1
        exec sp__run_cmd ''del /q %temp%\test.txt'',@dbg=1

        -- simple test with table
        create table #src (lno int identity(10,10),line nvarchar(4000))
        exec sp__script ''sp__script'',''#src''
        exec sp__file_write ''%temp%\test.txt'',@table=''#src'',@addcrlf=1,@dbg=1
        exec sp__run_cmd ''type %temp%\test.txt'',@dbg=1
        drop table #src

        -- unicode test (don''t work)
        declare @st sysname set @st=''arabia ?????? lang?''
        exec sp__file_write ''%temp%\test.txt'',@text=@st
        exec sp__run_cmd ''type %temp%\test.txt'',@dbg=1

*/
CREATE proc [dbo].[sp__file_write]
    @file nvarchar(1024)=null out,
    @text nvarchar(4000)=null,
    @table sysname =null,
    @addcrlf bit=0,
    @append bit=0,
    @unicode bit=0,
    @uid sysname=null,
    @pwd sysname=null,
    @dbg bit=0
as
begin -- proc
set nocount on
declare @proc sysname,@ret int,@n int,@i int
select @proc=object_name(@@procid),@ret=0
if @table is null and @text is null and @file is null goto help

-- sp__find ''@table=''
if not @table is null
and not @table in (''#src'',''#out'',''#ftpsrc'',''#htm'',''#html'')
    goto err_tbl

if @dbg=1 exec sp__printf ''-- %s ------------------------------'',@proc

if coalesce(@file,'''')='''' goto err_file

declare
    @cmd nvarchar(4000), @crlf nchar(2), @row nvarchar(4000),
    @sql nvarchar(4000), @tmp sysname, @order nvarchar(512),
    @flds nvarchar(4000),@fldsc nvarchar(4000),
    @olecmd sysname,
    @source nvarchar(255),
    @description nvarchar(255),
    @helpfile nvarchar(255),
    @helpid int

select @crlf = crlf from fn__sym()

declare
    @objfilesystem int,@objtextstream int,@objerrorobject int,
    @strerrormessage nvarchar(1000),@hr int

if charindex(''%temp%'',@file)>0 begin
    exec sp__get_temp_dir @row out
    set @file=replace(@file,''%temp%'',@row)
    if @dbg=1 print @file
end

if left(@file,1)=''"'' select @file=substring(@file,2,4000)
if right(@file,1)=''"'' select @file=left(@file,len(@file)-1)

if not @table is null
    begin
    if @table=''#out''
        declare cs cursor local for
            select line
            from #out
            order by lno
    if @table=''#src''
        declare cs cursor local for
            select line
            from #src
            order by lno
    if @table=''#ftpsrc''
        declare cs cursor local for
            select line
            from #ftpsrc
            order by lno
    if @table=''#htm''
        declare cs cursor local for
            select line
            from #htm
            order by lno
    if @table=''#html''
        declare cs cursor local for
            select line
            from #html
            order by lno
    open cs
    end -- table source

/*
    see: http://msdn.microsoft.com/en-us/library/314cz14s(VS.85).aspx
    object.OpenTextFile(filename[, iomode[, create[, format]]])
    object:Required. Object is always the name of a FileSystemObject.
    filename:Required. String expression that identifies the file to open.
    iomode:Optional. Can be one of three constants: ForReading, ForWriting, or ForAppending.
    create:Optional. Boolean value that indicates whether a new file can be created if the specified filename doesn''t exist. The value is True if a new file is created, False if it isn''t created. If omitted, a new file isn''t created.
    format:Optional. One of three Tristate values used to indicate the format of the opened file. If omitted, the file is opened as ASCII.
    Settings:The iomode argument can have any of the following settings:
         ForReading|1|Open a file for reading only. You can''t write to this file.
         ForWriting|2|Open a file for writing.
         ForAppending|8|Open a file and write to the end of the file.
    The format argument can have any of the following settings:
         TristateUseDefault|-2|Opens the file using the system default.
         TristateTrue|-1|Opens the file as Unicode.
         TristateFalse|0|Opens the file as ASCII.
*/

select @strerrormessage=''opening the file system object''
select @olecmd=''scripting.filesystemobject''
execute @hr = sp_oacreate  @olecmd, @objfilesystem out
if @hr!=0 goto err_ole

if @append=0
    begin
    if @dbg=1 exec sp__printf ''creating file "%s" unicode(%d)'',@file,@unicode
    select @olecmd=''createtextfile''
    execute @hr = sp_oamethod   @objfilesystem   , @olecmd, @objtextstream out, @file,2,@unicode
    if @hr!=0 goto err_ole
    end -- crete
if @append=1
    begin
    if @dbg=1 exec sp__printf ''opening file "%s" unicode(%d)'',@file,@unicode
    select @olecmd=''opentextfile''
    execute @hr = sp_oamethod   @objfilesystem   , @olecmd, @objtextstream out, @file,8,@unicode,-1
    if @hr!=0 goto err_ole
    end -- oepn for append

while 1=1 begin
    if not @table is null begin
        fetch next from cs into @text
        if @@fetch_status!=0 break
    end
    /* -- old version:
    set @i=1
    set @n=dbo.fn__str_count(@text,@crlf)
    while (@i<=@n) begin
        set @row=dbo.fn__str_at(@text,@crlf,@i)
        set @cmd=''echo ''+@row+'' 1>>"''+@file+''"''
        if @dbg=1 print @cmd
        exec master.dbo.xp_cmdshell @cmd,no_output
        set @i=@i+1
    end -- while
    */
    if @addcrlf=1 set @text=@text+@crlf
    select @olecmd=''write''
    execute @hr = sp_oamethod  @objtextstream, @olecmd, null, @text
    if @hr!=0 goto err_ole
    if @table is null break
end -- while write table
select @olecmd=''close''
execute @hr = sp_oamethod  @objtextstream, @olecmd
if @hr!=0 goto err_ole

if not @table is null begin
    close cs
    deallocate cs
end

execute sp_oadestroy @objtextstream
execute sp_oadestroy @objfilesystem
goto ret

-- =================================================================== errors ==

err_file:   exec @ret=sp__err ''file not specified'',@proc goto ret
err_db:     exec @ret=sp__err ''%s must specified after each from'',@proc,@p1=''%db%'' goto ret
err_tbl:    exec @ret=sp__err ''accepted table are #out and #src'',@proc goto ret
err_bcp:    exec @ret=sp__err ''error during generation of xls (%s)'',@proc,@p1=@row goto ret
err_ole:    if @olecmd=''scripting.filesystemobject''
                select @objerrorobject=@objfilesystem
            else
                begin
                select @objerrorobject=@objtextstream
                if @objerrorobject=0
                    select @objerrorobject=@objfilesystem
                end
            execute sp_oageterrorinfo  @objerrorobject,
                @source output,@description output,
                @helpfile output,@helpid output
            exec @ret=sp__err ''file:%s; cmd:%s; src:%s; desc:%s'',
                              @proc,@p1=@file,@p2=@olecmd,@p3=@source,@p4=@description
            execute sp_oadestroy @objtextstream
            execute sp_oadestroy @objfilesystem
            goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    write or append to a text file a string or a table

Parameters
    @file       name of destination file
    @table        optional table name;
                can be only one of this tables:
                #src,#out,#ftpsrc,#htm,#html
    @text       optional line of text to write
    @addcrlf    0 by default, if 1 add a crlf to each line
    @append     0 by default, append to existing file instead of create
    @unicode    0 by default, 1 to write a unicode file instead of ANSI/ASCII
    @uid        not used
    @pwd        not used
''

ret:
return @ret
end -- sp__file_write'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__file_write:

-- ========================================================= sp__file_write_blob
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__file_write_blob',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131017
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__file_write_blob') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__file_write_blob') with nowait
        goto skip_sp__file_write_blob
        end
    if @ver>131017
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__file_write_blob') with nowait
        goto skip_sp__file_write_blob
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__file_write_blob') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__file_write_blob'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__file_write_blob]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    c:originally from www.sql.ru (mythical boys)
    v:131017\s.zaglio: about help and tests
    v:130803\s.zaglio: review
    v:100104\s.zaglio: save blob to a binary file
    t:sp__file_write_blob_test 100000000
    t:sp__file_write_blob_test @opt=small
    t:sp__file_write_blob_test @opt=big
    t:sp__file_write_blob_test @opt=huge
*/
CREATE proc [dbo].[sp__file_write_blob]
    @sqlfield nvarchar(4000) = null,
    @file sysname = null,
    @uid sysname = null,
    @pwd sysname = null,
    @blob varbinary(max) = null,
    @dbg bit=0
as
begin
set nocount on
declare
    @proc sysname,@ret int
select
    @proc=object_name(@@procid),@ret=0

declare
    @sql nvarchar(4000),@tmp nvarchar(512),
    @hr int,@obj int,@n int,@buffer int,
    @i int,@chunk varbinary(max)            -- split too big blob

if (nullif(@sqlfield,'''') is null and @blob is null) or @file is null goto help
if @file like ''%..%'' goto err_wpro

exec sp__get_temp_dir @tmp out
select @file=replace(@file,''%temp%'',@tmp)
if @dbg=1 exec sp__printf ''f:%s'',@file

if @sqlfield!=''''
    begin
    if left(@sqlfield,7)=''select ''
        begin
        select @sql=''select @blob=(''+@sqlfield+'')''
        exec sp_executesql @sql,N''@blob varbinary(max) out'',@blob=@blob out
        end
    else
        goto err_fld
    end

-- types: 1=adtypebinary
-- tsave: 2=adsavecreateoverwrite
select @obj=0,@buffer=64*1024--*1024
exec @hr=sp_oacreate ''adodb.stream'', @obj output; if @hr!=0 goto err
exec @hr=sp_oasetproperty @obj, ''type'', 1; if @hr!=0 goto err
exec @hr=sp_oamethod @obj, ''open''; if @hr!=0 goto err
select @n=datalength(@blob),@i=1
while (@i<@n)
    begin
    select @chunk=substring(@blob,@i,@buffer)
    exec @hr=sp_oamethod @obj, ''write'', null, @chunk; if @hr!=0 goto err
    exec @hr=sp_oamethod @obj, ''flush''; if @hr!=0 goto err
    select @i=@i+@buffer
    end
exec @hr=sp_oamethod @obj, ''savetofile'',null,@file,2; if @hr!=0 goto err
exec @hr=sp_oamethod @obj, ''close''; if @hr!=0 goto err
exec @hr=sp_oadestroy @obj;
select @obj=0
goto ret


-- =================================================================== errors ==

err:
declare @source nvarchar(255)
declare @description nvarchar(255)

exec @hr = sp_oageterrorinfo @obj, @source out, @description out
exec @ret=sp__err ''%s;%s'',@proc,@p1=@source,@p2=@description

if @obj!=0 exec sp_oadestroy @obj
goto ret

err_wpro:
exec @ret=sp__err ''worked protection against hackers :-)'',@proc
goto ret

err_fld:
exec @ret=sp__err ''@sqlfield must be a select'',@proc
goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    save a blob to a file

See also
    * sp__file_read_blob

Parameters
    @sqlfield   providede for back compatibility,
                is the select the retrieve the blob
    @file       destination path (support %temp%)
    @uid        not used, provided for old compatibility
    @pwd        not used, provided for old compatibility
    @blob       direct data to save

''
select @ret=-1
goto ret

-- ===================================================================== exit ==
ret:
return @ret
end -- [sp__file_write_blob]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__file_write_blob:

-- ==================================================== sp__file_write_blob_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__file_write_blob_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131017
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__file_write_blob_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__file_write_blob_test') with nowait
        goto skip_sp__file_write_blob_test
        end
    if @ver>131017
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__file_write_blob_test') with nowait
        goto skip_sp__file_write_blob_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__file_write_blob_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__file_write_blob_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__file_write_blob_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131017\s.zaglio: test the sp__file_write_blob
*/
CREATE proc sp__file_write_blob_test
    @size int = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @huge bit,@small bit,@big bit,@d datetime

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    @huge=charindex(''|huge|'',@opt),
    @small=charindex(''|small|'',@opt),@big=charindex(''|big|'',@opt)

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if cast(@huge as int)+@small+@big>1 raiserror(''please give only one option'',16,1)
if @size is null and cast(@huge as int)+@small+@big=0 goto help

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

declare @v varbinary(max)

if @small=1 select @size=3998
if @big=1 select @size=11998
if @huge=1 select @size=200000000

exec sp__elapsed @d out,''Begin test''
select @v=cast(
            ''a''+replicate(cast(''b'' as varchar(max)),@size)+
            ''z'' as varbinary(max))
select @size=len(@v)
exec sp__elapsed @d out,''after generating blob a...z of %d bytes'',@v1=@size

exec sp__file_write_blob
        @file=''%temp%\sp__file_write_blob_test.txt'',
        @blob=@v

exec sp__elapsed @d out,''after write''

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test sp__file_write_blob, writing a huge text file (2gb)
    to %temp%\sp__file_write_blob_test.txt

Parameters
    [param]     [desc]
    @size       dynamic number of bytes to test
    @opt        options
                small   test a 4000 bytes blob
                big     test a 12000 bytes blob
                huge    test a 2.000.000.002 bytes blob
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__file_write_blob_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__file_write_blob_test:

-- ======================================================= sp__file_write_stream
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__file_write_stream',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121118
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__file_write_stream') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__file_write_stream') with nowait
        goto skip_sp__file_write_stream
        end
    if @ver>121118
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__file_write_stream') with nowait
        goto skip_sp__file_write_stream
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__file_write_stream') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__file_write_stream'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__file_write_stream]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:121118\s.zaglio: added out option and info to skip BOM
    v:121026\s.zaglio: added @txt
    v:120723\s.zaglio: added html opt
    v:110621\s.zaglio: write a text file with different format
    t:
        declare @d datetime
        create table #src(lno int identity,line nvarchar(4000))
        insert #src select name+'' ''+name+'' ''+name+'' ''+name from syscolumns
        exec sp__elapsed @d out,''Init''
        exec sp__file_write_stream ''%temp%\stream_test1.txt''
        exec sp__elapsed @d out,''after stream in utf8''
        exec sp__file_write ''%temp%\stream_test2.txt'',@table=''#src'',@addcrlf=1,@unicode=1
        exec sp__elapsed @d out,''after fileobject in unicode''
        exec master..xp_cmdshell ''del /q /f %temp%\stream_test1.txt'',no_output
        exec master..xp_cmdshell ''del /q /f %temp%\stream_test2.txt'',no_output
        drop table #src
*/
CREATE proc [dbo].[sp__file_write_stream]
    @path nvarchar(1024)=null,
    @fmt sysname        =null,
    @sep nvarchar(2)    =null,
    @txt nvarchar(max)  =null,
    @opt sysname        =null,
    @dbg int            =0
as
begin
set nocount on
declare @proc sysname,@ret int
select
    @proc=object_name(@@procid),@ret=0,
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''||'')

if @fmt is null select @fmt=''utf-8''
if @txt is null and @sep is null select @sep=crlf from fn__sym()

-- ============================================================= declarations ==
declare
    @tmp nvarchar(512),@cmd sysname,@hr int,@obj int,@line nvarchar(4000),
    @html bit,@src bit,@out bit,@ll int

-- ===================================================================== init ==

select
    @src=isnull(object_id(''tempdb..#src''),0),
    @html=charindex(''|html|'',@opt),
    @out=charindex(''|out|'',@opt)

if @out=1 and object_id(''tempdb..#out'') is null goto err_out
if @html=1 and object_id(''tempdb..#html'') is null goto err_htm
if not @txt is null and not @sep is null goto err_txs

if @out=1 or @html=1 select @src=0      -- exclude #src of parent process

if @path is null and @src=0 and @html=0 and @out=0 and @txt is null
    goto help

if @path like ''%[%]temp[%]%''
    begin
    exec sp__get_temp_dir @tmp out
    select @path=replace(@path,''%temp%'',@tmp)
    if @dbg=1 exec sp__printf ''path:%s'',@path
    end

if @path like ''%..%'' goto err_wpro

select @cmd=''ADODB.Stream''
exec @hr = sp_oacreate @cmd, @obj out
if @hr!=0 goto err_ole
select @cmd=''Type''
exec @hr = sp_oasetproperty  @obj ,@cmd,2 -- text
if @hr!=0 goto err_ole
select @cmd=''charset''
exec @hr = sp_oasetproperty  @obj ,@cmd,@fmt
if @hr!=0 goto err_ole
select @cmd=''Open''
exec @hr = sp_oamethod  @obj , @cmd, null
if @hr!=0 goto err_ole
-- UTFStream.Position = 3 ''skip BOM
select @cmd=''WriteText''
if not @txt is null
    exec @hr = sp_oamethod  @obj , @cmd, null, @txt, 0
else
    begin
    if @html=1
        begin
        select @ll=max(lno) from #html
        declare cs cursor local for
            select isnull(line,'''')+case lno when @ll then '''' else @sep end
            from #html
            order by lno
        end
    if @src=1
        begin
        select @ll=max(lno) from #src
        declare cs cursor local for
            select isnull(line,'''')+case lno when @ll then '''' else @sep end
            from #src
            order by lno
        end
    if @out=1
        begin
        select @ll=max(lno) from #out
        declare cs cursor local for
            select isnull(line,'''')+case lno when @ll then '''' else @sep end
            from #out
            order by lno
        end

    open cs
    while 1=1
        begin
        fetch next from cs into @line
        if @@fetch_status!=0 break
        -- default line separator keep data attached
        exec @hr = sp_oamethod  @obj , @cmd, null, @line, 0
        -- todo: remove bom
        -- objStream.Position = objStream.Size ''write at the beginning of the stream .ie. overwrite all
        if @hr!=0 goto err_ole
        end -- while of cursor
    close cs
    deallocate cs
    end

/*
UTFStream.Position = 3 ''skip BOM

Dim BinaryStream As Object
Set BinaryStream = CreateObject("adodb.stream")
BinaryStream.Type = adTypeBinary
BinaryStream.Mode = adModeReadWrite
BinaryStream.Open

''Strips BOM (first 3 bytes)
UTFStream.CopyTo BinaryStream

''UTFStream.SaveToFile "d:\adodb-stream1.txt", adSaveCreateOverWrite
UTFStream.Flush
UTFStream.Close

BinaryStream.SaveToFile "d:\adodb-stream2.txt", adSaveCreateOverWrite
BinaryStream.Flush
BinaryStream.Close
*/
select @cmd=''SaveToFile''
exec @hr = sp_oamethod  @obj , @cmd, null, @path, 2 -- adSaveCreateOverwrite
if @hr!=0 goto err_ole

goto ret

-- =================================================================== errors ==
err_wpro:
exec @ret=sp__err ''worked protection against hackers :-) '',@proc
goto ret

err_txs:
exec @ret=sp__err ''@sep is not compatible with @txt'',@proc
goto ret

err_ole:
declare @source nvarchar(255)
declare @description nvarchar(255)
exec @hr = sp_oageterrorinfo @obj, @source out, @description out
exec @hr = sp_oadestroy @obj
select @obj=null
exec @ret=sp__err ''ole error (%s;%s;%s)'',@proc,@p1=@cmd,@p2=@source,@p3=@description
goto ret

err_htm:
exec @ret=sp__err ''#html not found'',@proc
goto ret

err_out:
exec @ret=sp__err ''#out not found'',@proc
goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,N''
Scope
    write a text file, using adodb.stream to allow charset types

Parameters
    #src    source text lines (lno int identity,line nvarchar(4000))
    #out    alternative source test lines if "out" option is specified
    #html   alternative source text lines
    @fmt    is the format of source file (default is "utf-8")
            See list below
    @sep    line separator (default is CRLF)
    @txt    a single full text varable
    @opt    options
            html    use #html instead of #src as source
            out     use #out instead of #src as source

Example
    create table #src(lno int identity,line nvarchar(4000))
    insert #src select ''''line one''''
    insert #src select N''''line with unicode Джон''''
    insert #src select ''''line tree''''
    exec sp__file_write_stream ''''%temp%\stream_test.txt'''',@dbg=1
    exec master..xp_cmdshell ''''type %temp%\stream_test.txt''''
    exec master..xp_cmdshell ''''del /q /f %temp%\stream_test.txt'''',no_output
    drop table #src

    declare @txt as nvarchar(max)
    declare @i int select @txt=N''''line with unicode Джон''''+char(13)+char(10),@i=16
    while @i>0 select @txt=@txt+@txt,@i=@i-1
    print datalength(@txt)
    select @txt=''''begin''''+char(13)+@txt+''''end''''
    exec sp__file_write_stream ''''%temp%\stream_test.txt'''',@txt=@txt
    exec xp_cmdshell ''''dir %temp%\stream_test.txt''''
    -- check on server and then delete file

-- List of accepted formats --
''
create table #fmts(line sysname)
insert #fmts
    exec master..xp_regenumkeys ''HKEY_CLASSES_ROOT'',''MIME\Database\Charset''
exec sp__select_astext ''select line from #fmts order by 1''
drop table #fmts

select @ret=-1
goto ret

ret:
if @obj!=0 exec @hr = sp_oadestroy @obj
set nocount off
return @ret
end -- [sp__file_write_stream]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__file_write_stream:

-- ==================================================================== sp__find
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__find',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140122
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__find') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__find') with nowait
        goto skip_sp__find
        end
    if @ver>140122
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__find') with nowait
        goto skip_sp__find
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__find') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__find'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__find]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:140122\s.zaglio: commented search into tags
    r:130925\s.zaglio: used try-catch to ensure search continuity
    v:130612\s.zaglio: adapted to new fn__sym
    v:121025\s.zaglio: added option distinct
    v:120827\s.zaglio: added autoscale to %word% if nothing is found
    v:120726.1600\s.zaglio: help,svn context and added func/sub name in list
    v:120614.1715\s.zaglio: exclude "-" from symbols and managed [word]
    v:120601\s.zaglio: a bug in search of exact col
    v:120229\s.zaglio: extended correctly search to obj & cols name
    v:120208\s.zaglio: inverted logic and option NOSVN to SVN
    v:120207\s.zaglio: again a small bug near existance of db utility
    v:120203\s.zaglio: added #sp__find_out and removed a small bug
    v:120127\s.zaglio: done
    r:120125\s.zaglio: working
    r:120123\s.zaglio: adding svn management
    v:120116\s.zaglio: added @range and restyled
    v:111216\s.zaglio: added "s" as separator symbol
    v:111116\s.zaglio: about help
    v:111031\s.zaglio: removed @dbs, added opt "script" and improved generic search
    v:111011\s.zaglio: added () as symbols
    v:111003\s.zaglio: added tags search and correct a bug near @m2=@m3
    v:110920\s.zaglio: added tab symbol
    v:110914\s.zaglio: added symbols : and \, @obj and @xtype
    v:110713\s.zaglio: a bug near code search
    v:110712\s.zaglio: added sep. symbols .#10#13#32
    v:110623\s.zaglio: added table name near columns
    v:110331\s.zaglio: optimized
    v:110530\s.zaglio: remake
    t:sp__find ''sp__find'',@dbg=1
    t:sp__find ''sp__job_status''
    t:sp__find ''PDA_DISABLE_MULTI_LABELLING'',@opt=''txt''
    t:sp__find ''[utility]'',@dbg=2
    t:sp__find ''%[utility]'',@dbg=2
*/
CREATE PROC sp__find
    @what  sysname = null,
    @obj   sysname = null,
    @xtype sysname = null,
    @range int     = null,
    @opt   sysname = null,
    @dbg   int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|''),
    @obj=dbo.fn__str_quote(@obj,''%'')
-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    @m1 sysname,@m2 sysname,@m3 sysname,@m4 sysname,
    @left sysname,@right sysname,@db sysname,@sym sysname,
    @sql nvarchar(4000),@params nvarchar(4000),
    @dbs sysname,@tags bit,@rows int,@typ sysname,
    @cmd nvarchar(1024),@url nvarchar(1024),@tmp nvarchar(4000),
    @file sysname,@d datetime,@n bigint,
    @id int,@rid int,@rev sysname,
    @crlf nvarchar(4),@cr nvarchar(2),@lf nvarchar(2),@tab nvarchar(2),
    @target nvarchar(1024),@path nvarchar(1024),
    @svn_export nvarchar(1024),@svn_dir nvarchar(1024),
    @svn_list nvarchar(1024), @svn_del_target nvarchar(1024),
    @svn_diff nvarchar(1024),@i int,@emsg nvarchar(2048),
    @svn bit                -- nosvn option


declare @stdout table (lno int identity primary key, line nvarchar(4000))
declare @files table (id int null,rev int,name nvarchar(1024))
declare @paths table (name nvarchar(1024))

create table #sql(id int identity,typ sysname,sql nvarchar(4000))
create table #src(lno int identity,line nvarchar(4000))
create table #dbs(db sysname)
create table #sysobjects(id int,[name] sysname,xtype nvarchar(4))

if object_id(''tempdb..#tmp_found'') is null
    create table #tmp_found(
        db sysname null,
        id sql_variant null, number int null, colid int null,
        p1 int null,p2 int null,p3 int null,
        obj sysname null,
        xtype sysname null,
        [txt1_or_obj] sysname null,
        [txt2_or_type] sysname null,
        txt3 sysname null
        )

-- =========================================================== initialization ==

select
    @cr=cr,@lf=lf,@tab=tab,@crlf=crlf,
    @sym=bounds,
    @cmd=''svn'',
    @target=''%temp%\''+replace(cast(newid() as sysname),''-'',''_'')+''.txt''
from fn__sym()

if charindex(''|dbs|'',@opt)>0
    begin
    select @dbs=''%''
    insert into #dbs(db)
    select [name]
    from master..sysdatabases
    where [name] like @dbs
    end
else
    insert #dbs(db) select db_name()

-- NB: escape not exists for patindex so I replace ] with ¬
select
    @what=  case
            when left(@what,1)=''['' and right(@what,1)='']''
            then replace(replace(@what,'']'',''¬''),''['',''[[]'')
            else @what
            end,
            -- dbo.fn__str_unquote(@what,''[]''),
    @tags=  charindex(''|tags|'',@opt),
    @range= case
            when @range is null
            then
                case
                when len(@what)+1>20
                then len(@what)+1
                else 20
                end
            else @range
            end,
    @left=  case
            when left(@what,1) like ''[0-9a-Z]''
              or left(@what,1) = ''[''
            then ''%[ [''+@sym+'']''
            else ''%''
            end,
    @right= case
            when right(@what,1) like ''[0-9a-Z]''
              or right(@what,1) = ''[''
            then ''[ [''+@sym+'']%''
            else ''%''
            end

select
    @m1=@left+@what+@right, -- single word
    @m2=@what+@right,
    @m3=@left+@what,
    @params=''@what sysname,@m1 sysname,@m2 sysname,@m3 sysname,''
           +''@range int,@db sysname,@xtype sysname,@obj sysname,''
           +''@err int out,@rows int out,@dbg int''

-- ======================================================== second params chk ==
if @what is null goto help

-- ===================================================================== body ==

if @dbg>0 exec sp__printf ''declare
    @what sysname,@xtype sysname,@m1 sysname,@m2 sysname,@m3 sysname,
    @db sysname,@obj sysname

select
    @what=  ''''%s'''',
    @m1=    ''''%s'''',
    @m2=    ''''%s'''',
    @m3=    ''''%s''''
    '',@what,@m1,@m2,@m3

-- ##########################
-- ##
-- ## svn
-- ##
-- ########################################################

if charindex(''|svn|'',@opt)>0
or left(@what,4)=''svn:''
    select @svn=1
else
    select @svn=0

-- svn file caching uses only one common table on utility db
if @svn=1
and exists(
    select null
    from master..sysdatabases
    where name=''utility''
    )
    begin

    if object_id(''utility..fnd'') is null
        begin
        -- drop table fnd
        exec(''
        create table utility..fnd(
            tid tinyint not null,   -- filename or text line
            id int identity constraint pk_fnd primary key,
            rid int not null,
            pid int not null,
            idx int not null,       -- file row number
            [key] int,              -- revision
            val nvarchar(4000)      -- name
            )
        create index ix_fnd on fnd(tid,rid,id)
        '')
        end -- fnd creation

    if db_name()!=''utility'' and object_id(''fnd'') is null
        exec(''create view fnd as select * from utility..fnd'')

    end -- creation of table or alias
else
    goto search

if object_id(''fnd'') is null
    begin
    select @svn=0
    goto search
    end

-- list or load new path, or refresh path
if left(@what,4)=''svn:''
    insert @paths select @what
else
    -- refresh previous cached paths
    insert @paths
    select val from fnd,tids where fnd.tid=tids.url

-- for each new path or previous path
declare cs cursor local for
    select name
    from @paths
open cs
while 1=1
    begin
    fetch next from cs into @path
    if @@fetch_status!=0 break

    select
        @url=protocol+''://''+host+path,
        @file=page,
        @svn_export=@cmd+'' export "''+@url+''%file%" ''+@target+'' -r HEAD'',
        @svn_dir=@cmd+'' ls -r HEAD ''+@url,
        @svn_list=@cmd+'' ls -r HEAD -R -v ''+@url, -- username X --password Y
        @svn_diff=@cmd+'' diff -r ''+@rev+'':HEAD --summarize ''+@url,
        @svn_del_target=''del ''+@target
    from fn__parseurl(@path,default)

    if @dbg=1
        begin
        exec sp__printf ''svn_dir:%s'',@svn_dir
        exec sp__printf ''target:%s'',@target
        end -- dbg

    -- sp__find ''svn://svr/SOURCES'',@dbg=1
    if isnull(@file,'''')=''''
        begin
        exec master..xp_cmdshell @svn_dir
        continue
        end

    -- list and import files
    -- select * from fnd -- drop table fnd
    -- sp__find ''svn://svr/SOURCES/prg/trunk/*.vb'',@dbg=1

    select @rid=null,@d=null

    select
        @rid=id,
        @d=cast(cast(nullif([key],0) as binary(4)) as smalldatetime) /* ...
           ... contain last ins/update time */
    from fnd,tids
    where fnd.tid=tids.url
    and val=@path

    if @rid is null
        begin
        if charindex(''|clean|'',@opt)>0
            begin
            exec sp__printf ''-- nothing to clean''
            continue
            end -- clean

        -- list all files
        select @d=getdate()
                -- store path and time to not overlap multiple imports
        insert fnd(
            tid,rid,pid,idx,
            [key],val
            )
        select top 1
            tids.url,0 as rid,0 as pid,0 as idx,
            cast(cast(@d as smalldatetime) as binary(4)),       -- lock import
            @what
        from tids
        select @rid=@@identity

        exec sp__printf ''-- populating new path on id %d'',@rid

        insert @stdout exec master..xp_cmdshell @svn_list

        select top 1 @tmp=line
        from @stdout
        where line like ''svn:%''
        order by lno

        -- if there is an error, unlock (deleting) the id
        if @@rowcount>0
            begin
            delete from fnd where id=@rid
            goto err_svn
            end

        select @n=count(*) from @stdout where line like replace(@file,''*'',''%'')
        exec sp__elapsed @d out,''-- after get list to download %d files'',@v1=@n
        if @n=0 goto ret

        -- extract columns and filtered files
        insert @files(rev,name)
        select left(line,7) rev,substring(line,44,512) [file]
        from @stdout
        where not line is null
        and line like replace(@file,''*'',''%'')

        end -- populate files
    else
        begin
        -- sp__find ''svn://svr/SOURCES/prg/trunk/*.vb'',@dbg=1

        if not @d is null               -- if locked
            begin

            if left(@what,4)!=''svn:''
                begin
                -- skip msg if are searching
                select @d=null -- do not show svn times
                continue
                end

            if charindex(''|unlock|'',@opt)>0
                begin
                exec sp__printf ''-- unlocked''
                update fnd set [key]=0 where id=@rid
                select @d=null  -- this skipp next if and not show svn times
                end

            if datediff(hh,@d,getdate())>1
                begin
                exec sp__elapsed @d,''-- automatic unlock of importing "%s"(id:%d) from'',
                                 @v1=@path,@v2=@rid
                update fnd set [key]=0 where id=@rid
                end
            else
                begin
                exec sp__elapsed @d,''-- already importing "%s"(id:%d) from'',
                                 @v1=@path,@v2=@rid
                select @d=null -- do not show svn times
                continue
                end
            end

        select @d=getdate()
        if charindex(''|clean|'',@opt)>0
            begin
            delete from fnd where pid=@rid
            select @n=@@rowcount
            exec sp__elapsed @d,''-- cleaned %d rows in'',@v1=@n
            select @d=null -- do not show svn times
            continue
            end -- clean

        update fnd set
            [key]=cast(cast(@d as smalldatetime) as binary(4)) -- lock update
        where id=@rid

        -- search for last
        select @rev=max([key])
        from fnd,tids
        where fnd.tid=tids.obj and rid=@rid

        if left(@what,4)=''svn:''
            exec sp__printf ''-- get updated files from revision %s for path %s'',
                            @rev,@path

        insert @stdout exec master..xp_cmdshell @svn_diff

        if exists(
            select top 1 null
            from @stdout
            where line like ''svn:%''
            order by lno
            )
            begin
            continue -- revision not found
            update fnd set [key]=0 where id=@rid
            end

        delete from @stdout

        -- list all files
        insert @stdout exec master..xp_cmdshell @svn_list

        -- extract columns
        insert @files(rev,name)
        select left(line,7) rev,substring(line,44,512) [file]
        from @stdout
        where not line is null
        and line like replace(@file,''*'',''%'')

        -- unlist files of same revision
        delete f
        from @files f
        join fnd on f.name=fnd.val
        cross join tids
        where fnd.tid=tids.obj
        and rid=@rid
        and f.rev=fnd.[key]

        -- update ref id of existing files so can be replaced
        update f set id=fnd.id
        from @files f
        join fnd on f.name=fnd.val
        cross join tids
        where fnd.tid=tids.obj
        and rid=@rid

        end -- update path

    end -- while of cursor
close cs
deallocate cs

-- download files/refresh cache
if exists(select null from @files)
    begin
    select @i=0
    declare cs cursor local for
        select id,rev,name
        from @files
    open cs
    while 1=1
        begin
        fetch next from cs into @id,@rev,@file
        if @@fetch_status!=0 break

        -- get file from svn into a generic container
        select @tmp=replace(@svn_export,''%file%'',@file)
        if @dbg=1 exec sp__printf ''%s'',@tmp

        -- export file from svn
        delete from @stdout
        insert @stdout exec master..xp_cmdshell @tmp

        if not exists(
            select top 1 null
            from @stdout
            where line like ''A %.txt''
            order by lno
            )
            goto err_svn

        -- import generic container
        truncate table #src
        exec @ret=sp__file_read_stream @target,@out=''#src''

        if @ret=0
            begin
            if not @id is null
                -- replace old version
                delete from fnd where rid=@id

            select @id=null

            insert fnd(tid,rid,pid,idx,[key],val)
            select tids.obj,@rid,@rid,0,@rev,@file
            from tids

            select @id=@@identity

            insert fnd(tid,rid,pid,idx,val)
            select tids.code,@id,@rid,src.lno,ltrim(rtrim(src.line))
            from #src src,tids
            where ltrim(rtrim(isnull(line,'''')))!=''''
            order by lno
            select @n=@@rowcount
            exec sp__printf ''-- downloaded %d lines from file %d:%s(r:%d)'',
                            @n,@i,@file,@rev
            select @i=@i+1
            end -- load ok

        end -- while of cursor
    close cs
    deallocate cs

    -- unlock import
    update fnd set [key]=0 where id=@rid

    exec master..xp_cmdshell @svn_del_target,no_output

    end -- download files

if not @d is null exec sp__elapsed @d,''-- svn elaboration in''

if left(@what,4)=''svn:''  goto ret

-- ##########################
-- ##
-- ## search
-- ##
-- ########################################################

search:

-- ##########################
-- ##
-- ## tags
-- ##
-- ########################################################
/*
insert #sql select ''tags'',''
use [%db%]
insert #tmp_found(
    db,id,obj,xtype,[txt1_or_obj],[txt2_or_type],txt3
    )
select
    @db db,
    obj_id,
    obj,
    tag+'''':'''',
    convert(sysname,val1) [txt1_or_obj],
    convert(sysname,val2) [txt2_or_type],
    convert(sysname,val3) txt3
from dbo.fn__script_info(default,''''gkvrt'''',default) tags
join sysobjects o on o.id=tags.obj_id
where 1=1
and not (val1 is null and val2 is null and val3 is null)
and (@xtype is null or o.xtype=@xtype)
and (  isnull(convert(sysname,val1),'''''''') like @m1
    or isnull(convert(sysname,val1),'''''''') like @m2
    or isnull(convert(sysname,val1),'''''''') like @m3
    or isnull(convert(sysname,val1),'''''''') = @what

    or isnull(convert(sysname,val2),'''''''') like @m1
    or isnull(convert(sysname,val2),'''''''') like @m2
    or isnull(convert(sysname,val2),'''''''') like @m3
    or isnull(convert(sysname,val2),'''''''') = @what

    or isnull(convert(sysname,val3),'''''''') like @m1
    or isnull(convert(sysname,val3),'''''''') like @m2
    or isnull(convert(sysname,val3),'''''''') like @m3
    or isnull(convert(sysname,val3),'''''''') = @what
    )
select @rows=@@rowcount
''
*/
-- ##########################
-- ##
-- ## code
-- ##
-- ########################################################
insert #sql select ''code'',''
use [%db%]
-- first fast pass filter
insert into #sysobjects(id,name,xtype)
select id,name,xtype
from sysobjects o with (nolock)
where 1=1
and (@xtype is null or ltrim(rtrim(o.xtype)) like @xtype)
and (@obj is null or o.name like @obj)
select @rows=@@rowcount
if @dbg=2 exec sp__printf ''''-- found %d sysobjs'''',@rows

-- second fast pass filter
declare @owhat sysname
select @owhat=replace(@what,''''¬'''','''']'''')
select c.id,number,colid,[text]
into #syscomments
from syscomments c with (nolock)
join #sysobjects o on o.id=c.id
where [text] like ''''%''''+@owhat+''''%''''
select @rows=@@rowcount
if @dbg=2 exec sp__printf ''''-- found %d syscomments'''',@rows

-- search into source
insert #tmp_found(db,id,number,colid,p1,p2,p3)
select
    @db,id,number,colid,
    patindex(@m1,replace([text],'''']'''',''''¬'''')) p1,
    patindex(@m2,replace([text],'''']'''',''''¬'''')) p2,
    patindex(@m3,replace([text],'''']'''',''''¬'''')) p3
from #syscomments
where patindex(@m1,replace([text],'''']'''',''''¬''''))>0
or patindex(@m2,replace([text],'''']'''',''''¬''''))>0
or patindex(@m3,replace([text],'''']'''',''''¬''''))>0

-- if nothing found, autoscale to %word%
if @@rowcount=0
    insert #tmp_found(db,id,number,colid,p1,p2,p3)
    select
        @db,id,number,colid,
        patindex(''''%''''+@owhat+''''%'''',[text]) p1,
        0 p2,
        0 p3
    from syscomments c with (nolock)
    where [text] like ''''%''''+@owhat+''''%''''

update #tmp_found set
    obj=o.name,
    xtype=o.xtype,
    [txt1_or_obj]=
        case when p1>0
        then substring(c.[text],p1-@range,p1+@range-p1+@range)
        else ''''''''
        end,
    [txt2_or_type]=
        case when p2>0
        then substring(c.[text],p2-@range,p2+@range-p2+@range)
        else ''''''''
        end,
    txt3=
        case when p3>0
        then substring(c.[text],p3-@range,p3+@range-p3+@range)
        else ''''''''
        end
from #tmp_found t
join #sysobjects o with (nolock) on t.id=o.id
join #syscomments c with (nolock) on c.id=t.id and c.number=t.number and c.colid=t.colid
select @rows=@@rowcount
''

-- ##########################
-- ##
-- ## objects
-- ##
-- ########################################################
insert #sql select ''objs'',''
use [%db%]
-- search into objects
insert #tmp_found(db,id,obj,xtype)
select @db,id,[name],xtype
from #sysobjects o with (nolock)
where patindex(@m1,replace(o.name,'''']'''',''''¬''''))>0
or patindex(@m2,replace(o.name,'''']'''',''''¬''''))>0
or patindex(@m3,replace(o.name,'''']'''',''''¬''''))>0
or o.name=@what
select @rows=@@rowcount
''

-- ##########################
-- ##
-- ## columns
-- ##
-- ########################################################
insert #sql select ''cols'',''
use [%db%]
insert #tmp_found(
    db,id,obj,xtype,[txt1_or_obj],[txt2_or_type]
    )
select @db,c.id,c.[name],t.name,o.name,o.xtype
from syscolumns c with (nolock)
join systypes t on c.xusertype=t.xusertype
left join sysobjects o with (nolock) on c.id=o.id
where patindex(@m1,replace(c.name,'''']'''',''''¬''''))>0
or patindex(@m2,replace(c.name,'''']'''',''''¬''''))>0
or patindex(@m3,replace(c.name,'''']'''',''''¬''''))>0
or @what=c.name
select @rows=@@rowcount
''

-- ##########################
-- ##
-- ## job steps
-- ##
-- ########################################################
insert #sql select ''jsteps'',''
use [%db%]
insert #tmp_found(
    db,id,colid,obj,xtype,[txt1_or_obj],p1,p2,p3
    )
select
    @db,j.job_id,s.step_id,
    j.name,
    ''''job.step'''',
    left(s.step_name,128),
    patindex(@m1,replace(s.step_name,'''']'''',''''¬'''')),
    patindex(@m2,replace(s.step_name,'''']'''',''''¬'''')),
    patindex(@m3,replace(s.step_name,'''']'''',''''¬''''))
from msdb..sysjobsteps s with (nolock)
join msdb..sysjobs j with (nolock)
on s.job_id=j.job_id
where database_name=@db
and (
       patindex(@m1,replace(s.step_name,'''']'''',''''¬''''))>0
    or patindex(@m2,replace(s.step_name,'''']'''',''''¬''''))>0
    or patindex(@m3,replace(s.step_name,'''']'''',''''¬''''))>0
    )
select @rows=@@rowcount
''

-- ##########################
-- ##
-- ## job commands
-- ##
-- ########################################################
insert #sql select ''jcmds'',''
use [%db%]
-- search into jobs command
insert #tmp_found(
    db,id,colid,obj,xtype,[txt1_or_obj],p1,p2,p3
    )
select
    @db as db,j.job_id as id,s.step_id as colid,
    left(j.name+''''.''''+s.step_name,128) as obj,
    ''''job.step.cmd'''' as xtype,
    left(s.command,128) as [txt1_or_obj],
    patindex(@m1,replace(s.command,'''']'''',''''¬'''')) as p1,
    patindex(@m2,replace(s.command,'''']'''',''''¬'''')) as p2,
    patindex(@m3,replace(s.command,'''']'''',''''¬'''')) as p3
from msdb..sysjobsteps s with (nolock)
join msdb..sysjobs j with (nolock)
on s.job_id=j.job_id
where database_name=@db
and (
       patindex(@m1,replace(s.command,'''']'''',''''¬''''))>0
    or patindex(@m2,replace(s.command,'''']'''',''''¬''''))>0
    or patindex(@m3,replace(s.command,'''']'''',''''¬''''))>0
    )

update #tmp_found set
    [txt1_or_obj]=
        case when p1>0
        then substring(s.command,p1-@range,p1+@range-p1+@range)
        else ''''''''
        end,
    [txt2_or_type]=
        case when p2>0
        then substring(s.command,p2-@range,p2+@range-p2+@range)
        else ''''''''
        end,
    txt3=
        case when p3>0
        then substring(s.command,p3-@range,p3+@range-p3+@range)
        else ''''''''
        end
from #tmp_found t
join msdb..sysjobsteps s with (nolock) on t.id=s.job_id and t.colid=s.step_id
where t.xtype=''''job.step.cmd''''
select @rows=@@rowcount
''

-- ##########################
-- ##
-- ## jobs name
-- ##
-- ########################################################
insert #sql select ''jobs'',''
insert #tmp_found(id,obj,xtype)
select
    j.job_id,
    j.name,
    ''''job''''
from msdb..sysjobs j with (nolock)
where j.name like @what
select @rows=@@rowcount
''

-- ##########################
-- ##
-- ## svn cache
-- ##
-- ########################################################
insert #sql select ''svn'',''
declare @d datetime select @d=getdate()
-- 1st fast pass filter
select c.rid as id,c.id as colid,[val] as [text],c.idx
into #syscomments
from fnd c with (nolock),tids
where c.tid=tids.code and [val] like ''''%''''+@what+''''%''''
if @dbg=1 exec sp__elapsed @d out,''''-- after 1st pass''''

-- search into source
declare @tobj tinyint
select @tobj=obj from tids
insert #tmp_found(id,colid,p1,p2,p3)
select
    id,colid,
    patindex(@m1,replace([text],'''']'''',''''¬'''')) p1,
    patindex(@m2,replace([text],'''']'''',''''¬'''')) p2,
    patindex(@m3,replace([text],'''']'''',''''¬'''')) p3
from #syscomments c
where patindex(@m1,replace([text],'''']'''',''''¬''''))>0
or patindex(@m2,replace([text],'''']'''',''''¬''''))>0
or patindex(@m3,replace([text],'''']'''',''''¬''''))>0
select @rows=@@rowcount
if @dbg=1 exec sp__elapsed @d out,''''-- after 2nd pass''''

-- extract functions/sub name and line range

if @dbg=1 exec sp__elapsed @d out,''''-- after 3rd pass''''

update #tmp_found set
    db=substring(
        o.val,
        charindex(''''://'''',o.val),
        dbo.fn__charindex(''''/'''',o.val,-1)-charindex(''''://'''',o.val)
        ),
    obj=substring(o.val,dbo.fn__charindex(''''/'''',o.val,-1)+1,128),
    xtype=''''svn'''',
    [txt1_or_obj]=f.obj,
    [txt2_or_type]=
        case f.typ when 1 then ''''sub'''' else ''''function'''' end,
    txt3=
        case
        when p1>0
        then substring(c.[text],p1-@range*2,p1+@range-p1+@range*2)
        when p2>0
        then substring(c.[text],p2-@range*2,p2+@range-p2+@range*2)
        when p3>0
        then substring(c.[text],p3-@range*2,p3+@range-p3+@range*2)
        else ''''''''
        end
from #tmp_found t
join fnd o with (nolock) on o.tid=@tobj and t.id=o.id   -- extract path
join #syscomments c with (nolock) on c.id=t.id and c.colid=t.colid
join #func f on c.idx between f.[from] and f.[to] and c.id=f.rid
if @dbg=1 exec sp__elapsed @d out,''''-- after 3rd pass''''
''

-- ===================================================== parse functions/subs ==
if @svn=1 select * into #func from (
    select
        a.rid,-- f.val as [file],
        1 typ,
        ltrim(substring(a.val,charindex(''sub '',a.val)+4,charindex(''('',a.val)-charindex(''sub '',a.val)-4)) as obj,
        a.idx [from],
        (select top 1 b.idx
         from fnd b
         where a.rid=b.rid and b.idx>a.idx
         and patindex(''end sub%'',ltrim(b.val))>0
         order by b.idx
        ) [to]
    from fnd a
    join fnd f on a.rid=f.id
    where left(ltrim(a.val),1)!=''''''''
    and patindex(''end sub%'',ltrim(a.val))=0
    and (patindex(''sub %'',a.val)>0
         or patindex(''% sub %'',a.val)>0
        )
    and patindex(''delegate %'',a.val)=0
    and patindex(''% delegate %'',a.val)=0
    and charindex(''('',a.val)>charindex(''sub '',a.val)

    union

    select
        a.rid,-- f.val as [file],
        0 typ,
        ltrim(substring(a.val,charindex(''function '',a.val)+9,charindex(''('',a.val)-charindex(''function '',a.val)-9)) as obj,
        a.idx [from],
        (select top 1 b.idx
         from fnd b
         where a.rid=b.rid and b.idx>a.idx
         and patindex(''end function%'',ltrim(b.val))>0
         order by b.idx
        ) [to]
    from fnd a
    join fnd f on a.rid=f.id
    where left(ltrim(a.val),1)!=''''''''
    and patindex(''end function%'',ltrim(a.val))=0
    and (patindex(''function %'',a.val)>0
         or patindex(''% function %'',a.val)>0
        )
    and patindex(''delegate %'',a.val)=0
    and patindex(''% delegate %'',a.val)=0
    and charindex(''('',a.val)>charindex(''function '',a.val)
    ) subs_and_functions

-- =========================================================== scan databases ==

declare cs cursor local for
    select db
    from #dbs
    where 1=1
open cs
while 1=1
    begin
    fetch next from cs into @db
    if @@fetch_status!=0 break

    declare css cursor local for
        select typ,replace([sql],''%db%'',@db)
        from #sql
        where (@tags=0)
        or (@tags=1 and typ=''tags'')
        order by id
    open css
    while 1=1
        begin
        fetch next from css into @typ,@sql
        if @@fetch_status!=0 break

        if @dbg=2 exec sp__prints @typ

        if @typ=''svn'' and @svn=0 continue

        begin try
        exec sp_executesql
                @sql,@params,
                @m1=@m1,@m2=@m2,@m3=@m3,@range=@range,@what=@what,@db=@db,
                @obj=@obj,@xtype=@xtype,@err=@err out,@rows=@rows out,
                @dbg=@dbg
        end try
        begin catch
        select @emsg=error_message()
        raiserror(@emsg,11,1)
        if @dbg>0 exec sp__printf ''error occurred into:''
        else exec sp__printf ''use @dbg=1 or @dbg=2 to see more info''
        if @dbg>0 exec sp__printsql @sql
        end catch
        if @err!=0 or @dbg=2 exec sp__printsql @sql
        if @dbg=2
            begin
            exec sp__printf ''-- found:%d rows'',@rows
            exec sp__select_astext ''select * from #tmp_found''
            exec sp__printf ''''
            truncate table #tmp_found
            end
        end -- sqls
    close css
    deallocate css

    end -- db loop

close cs
deallocate cs


-- show results
display:

if charindex(''|script|'',@opt)>0
    begin
    delete from #tmp_found where obj=''sp__find''
    select @sql=null
    select @sql=isnull(@sql+''|'','''')+obj
    from (
        select distinct obj
        from #tmp_found
        ) tmp
    order by obj
    exec sp__printf ''exec sp__drop ''''%s'''',@simul=0\nGO'',@sql
    declare cs cursor local for
        select distinct obj
        from #tmp_found
        -- where xtype in (''p'',''
        -- select * from fn__xtype()
        order by obj
    open cs
    while 1=1
        begin
        fetch next from cs into @obj
        if @@fetch_status!=0 break
        exec sp__script @obj
        exec sp__printf ''GO''
        end -- while of cursor
    close cs
    deallocate cs

    goto dispose
    end

-- =========================================================== output results ==

if @dbg=1 select * from #sysobjects order by name

if object_id(''tempdb..#sp__find_out'') is null
    begin
    if @dbg!=2
        begin
        if charindex(''|print|'',@opt)=0
        and charindex(''|html|'',@opt)=0
            begin
            select @sql=''
            select distinct db,obj,xtype,[txt1_or_obj],[txt2_or_type],txt3
            from #tmp_found a
            where not obj is null
            order by db,xtype,obj
            ''
            if charindex(''|distinct|'',@opt)!=0
                select @sql=replace(@sql,''[txt1_or_obj],[txt2_or_type],txt3'',''
                    (select top 1 [txt1_or_obj] from #tmp_found b where b.db=a.db and b.obj=a.obj) txt1_or_obj,
                    (select top 1 [txt2_or_type] from #tmp_found b where b.db=a.db and b.obj=a.obj) txt2_or_type,
                    (select top 1 [txt3] from #tmp_found b where b.db=a.db and b.obj=a.obj) txt3
                    '')
            exec(@sql)
            end
        else
            begin
            select @sql=''
                select distinct db,obj,xtype,[txt1_or_obj],[txt2_or_type],txt3
                from #tmp_found a
                where not obj is null
                order by 1,3,2
                ''
            if charindex(''|distinct|'',@opt)!=0
                select @sql=replace(@sql,''[txt1_or_obj],[txt2_or_type],txt3'',''
                    (select top 1 [txt1_or_obj] from #tmp_found b where b.db=a.db and b.obj=a.obj) txt1_or_obj,
                    (select top 1 [txt2_or_type] from #tmp_found b where b.db=a.db and b.obj=a.obj) txt2_or_type,
                    (select top 1 [txt3] from #tmp_found b where b.db=a.db and b.obj=a.obj) txt3
                    '')
            if charindex(''|html|'',@opt)=0
                exec sp__select_astext @sql -- ,@dbg=1
            else
                exec sp__select_astext @sql,@opt=''html'',@header=1
            end
        end
    end
else
    insert #sp__find_out(
        db,obj,xtype,[txt1_or_obj],[txt2_or_type],txt3
        )
    select distinct db,obj,xtype,[txt1_or_obj],[txt2_or_type],txt3
    from #tmp_found
    where not obj is null
    order by db,xtype,obj


dispose:
drop table #tmp_found
drop table #sysobjects
drop table #dbs

goto ret

-- =================================================================== errors ==
err_svn: exec @ret=sp__err ''wrong svn request (%s)'',@proc,@p1=@tmp goto ret

-- ===================================================================== help ==
help:

select @params=@cr+''|''+@lf+''|''+@tab+''|¬''
select @sym=replace(@sym,'' '',''{space}'')
exec sp__str_replace @sym out,@params,''{carrige}'',''{line feed}'',''{tab}'',''[]''
exec sp__usage @proc,''
Scope

    search for word/s between symbols
        >|
         %p1%
         |<

Notes

    * If begin or end with "%" a normal LIKE is used
    * Due limits of 4k''''s  MSSQL store method,
      if a word is split across two chunk (syscomments.text),
      will not found.
    * SVN data are cached into "utility..fnd" table and then refreshed
    automatically before every search.
    * sp__find uses a local "fnd" link to utility.fnd

Parameters

    @what   is the text to search
            or a svn path
    @obj    filter objects by %name% (can be multiple xx|yy|...)
    @xtype  filter for specific type (V,P,F_,etc.)
    @range  default 20, is the len of extracted chars before and after
            point where searched text was found

    @opt    options
            tags        search only info tags
                        (can add to MSSMS keyboard shortcut as
                         ctrl+? -> sp__find @opt=''''tags'''',@what=
                        )
            dbs         search in all databases
            script      script the objects found
            print       show result as text
            html        return a <table>...</table>
            distinct    show obj only once and the top 1 of txt1,2,3
            ------- svn options -----------------------------------------
            clean   delete all downloaded data for the specified svn path
            svn     enable update & search from svn cached data
            unlock  force unlock of (probable) broken "already importing"

    #sp__find_found     if exists, the output is stored here and not shown
                            create table #sp__find_out(
                                -- id int identity,
                                db sysname null,
                                obj sysname null,
                                xtype sysname null,
                                [txt1_or_obj] sysname null,
                                [txt2_or_type] sysname null,
                                txt3 sysname null
                                )
    @dbg    1: show preselected objects and likes @m1,@m2,@m3
            2: print code and relative results

Examples

    exec sp__find "sp__find"

    exec sp__find "moved"

    exec sp__find @opt="tags",@what="moved"

    exec sp__find "[utility]"       -- special search, as word "[utility]"
    exec sp__find "%[utility]"      -- this is an error because will search
                                       anithing that cotain u,t,i,l,y.

    exec sp__find "fn__test[1(]"
        -- search the calls to "fn__test(" or "sp__test1"
        -- usefull to replace old versions of a function

    -- ########## SVN ########## --
    exec sp__find "svn://svr/SOURCES"         -- list content recursivelly
    exec sp__find "svn://svr/SOURCES/*.vb"    -- set and do a download
                                              -- or update existing

-- list of svn downloads --
'',@p1=@sym

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__find'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__find:

-- =============================================================== sp__flds_list
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__flds_list',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=151106
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__flds_list') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__flds_list') with nowait
        goto skip_sp__flds_list
        end
    if @ver>151106
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__flds_list') with nowait
        goto skip_sp__flds_list
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__flds_list') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__flds_list'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__flds_list]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:table,field,list,quote,name,synonym
    v:151106\s.zaglio: added collate database_default
    v:121118\s.zaglio: added quote
    v:121003\s.zaglio: return list of flds even of a synonym
    d:121003\s.zaglio: fn__flds_list
    d:121003\s.zaglio: sp__flds_of
    d:121003\s.zaglio: sp__search
    t:sp__flds_list null,''cfg'',@dbg=1,@exclude=''%id%''
    t:select * into #cfg from cfg
    t:sp__flds_list null,''#cfg'',@dbg=1,@exclude=''%id%''
*/
CREATE proc sp__flds_list
    @flds nvarchar(4000) = null out,
    @obj sysname = null,
    @sep nvarchar(32) = null,
    @exclude nvarchar(4000) = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==
if isnull(@obj,'''')='''' goto help

-- ============================================================== declaration ==
declare
    -- generic common
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @opt1 bit,@opt2 bit,
    @cols nvarchar(4000),@db sysname,@i int,
    @sql nvarchar(4000),@oid int,
    @params nvarchar(512),
    @end_declare bit

-- =========================================================== initialization ==
-- try to resolve synonym or synonym of synonym...
select
    @flds=null,
    @obj=case when left(@obj,1)=''#'' then ''tempdb..''+@obj else @obj end,
    -- @opt1=charindex(''|opt|'',@opt),
    @sep=isnull(@sep,''|''),
    @end_declare=1

-- follow synonym
exec sp__script_synonym @obj out,@obj,@opt=''path''
select @db=isnull(parsename(@obj,3),db_name())

-- ======================================================== second params chk ==


-- ===================================================================== body ==

-- print parsename(''[db..obj]'',1)
-- print parsename(''db..[obj]'',1)

select @oid=object_id(@obj)
if @oid is null return null

/*  this fn is so slower that is better
    delete excludes to the end instead of while */

-- collect cols
select @sql=replace(''
select @flds=isnull(@flds+@sep,'''''''')
            +case
             when dbo.fn__token_sql(c.name)=1
               or patindex(''''%[^a-z0-9]%'''',c.name collate database_default)>0
             then quotename(c.name)
             else c.name
             end
from [%db%].sys.columns c
where c.object_id=@oid
and not case c.is_identity when 1 then ''''%id%'''' else c.name collate database_default end in (
    select token from fn__str_table(@exclude,@sep)
    )
order by column_id
'',''%db%'',@db)
if @dbg=1 exec sp__printsql @sql
select @params=''@flds nvarchar(4000) out,@sep nvarchar(32),''
              +''@oid int,@exclude nvarchar(4000)''
-- exec sp__printsql @sql
exec sp_executesql @sql,
                   @params,@flds=@flds out,@sep=@sep,@exclude=@exclude,@oid=@oid

-- sp__flds_list null,''#cfg'',@dbg=1,@exclude=''%id%''
-- select * into #cfg from cfg  -- drop table #cfg
-- select * from [tempdb].sys.columns c where object_id=object_id(''tempdb..#cfg'')
if @dbg=1 exec sp__printf ''-- db:%s, obj:%s, flds:%s'',@db,@obj,@flds

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
/*
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_me1:    select @e_msg=''write here msg''                              goto err
err_me2:    select @e_msg=''write this %s'',@e_p1=@var                    goto err
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    return list of flds even of a synonym

Notes
    fields with symbols or reserved words will be quoted

Parameters
    @flds       out variable
    @obj        table or synonym
    @sep        separator (default is pipe |)
    @excludes   list os fields to exclude, separated by @sep
                field %id% is a macro that means "identity field"
    @opt        options (not used)

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__flds_list'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__flds_list:

-- ============================================================ sp__flds_of_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__flds_of_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130730
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__flds_of_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__flds_of_test') with nowait
        goto skip_sp__flds_of_test
        end
    if @ver>130730
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__flds_of_test') with nowait
        goto skip_sp__flds_of_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__flds_of_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__flds_of_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__flds_of_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:130730\s.zaglio: test fn__flds_of
*/
CREATE proc sp__flds_of_test
as
begin
set nocount on
declare @ret int,@proc sysname
select @ret=0,@proc=object_name(@@procid)

declare
    @d datetime,@i int,@flds nvarchar(4000),
    @id int,@tbl sysname,@sep char,@exclude nvarchar(4000),
    @result nvarchar(4000),@st sysname,@new_result nvarchar(4000)

if not object_id(''tst_fn__flds_of'') is null drop table tst_fn__flds_of
create table tst_fn__flds_of(
    id int identity,
    a int, b bit, c nvarchar(max),
    [_0] uniqueidentifier
    )
select * into #tst_fn__flds_of from tst_fn__flds_of

declare @test table(
    id int primary key,
    tbl sysname,
    sep char,
    exclude sysname null,
    result nvarchar(4000)
    )

insert @test select 1,''#tst_fn__flds_of'','','',null,''id,a,b,c,_0''
insert @test select 2,''#tst_fn__flds_of'','','',''id,a'',''b,c,_0''
insert @test select 3,''#tst_fn__flds_of'','','',''%id%'',''a,b,c,_0''
insert @test select 4,''tst_fn__flds_of'','','',''a,%id%'',''b,c,_0''
insert @test select 5,''unk'','','',null,null

declare cs cursor local for select id,tbl,sep,exclude,result from @test
open cs
while 1=1
    begin
    fetch next from cs into @id,@tbl,@sep,@exclude,@result
    if @@fetch_status!=0 break

    select @new_result=dbo.fn__flds_of(@tbl,@sep,@exclude)
    if @new_result=@result
    or @new_result is null and @result is null
        select @st=''ok'' else select @st=''ko''
    exec sp__printf ''%d:%s\nexpected:%s\nresult:%s'',
                    @id,@st,@result,@new_result
    if @st=''ko'' select @ret=1

    end -- cursor cs
close cs
deallocate cs

exec sp__printf ''''
exec sp__elapsed @d out
select @i=1000
while @i>0
    select  @flds=dbo.fn__flds_of(''tst_flds_of'','','',null),
            @i=@i-1
exec sp__elapsed @d out,''after 1000 loops of v:120413 with ident''
select @i=1000
while @i>0
    select  @flds=dbo.fn__flds_of(''tst_flds_of'','','',''%id%''),
            @i=@i-1
exec sp__elapsed @d out,''after 1000 loops of v:120413 without ident''
-- before 120413: 1s 63ms
-- after 120413: 1s 540ms and 2s 433ms without

drop table #tst_fn__flds_of
drop table tst_fn__flds_of

if @ret!=0 exec sp__err ''test failed'',@proc

return @ret
end -- sp__flds_of_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__flds_of_test:

-- ====================================================================== sp__fn
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__fn',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090707.1055
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__fn') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__fn') with nowait
        goto skip_sp__fn
        end
    if @ver>090707.1055
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__fn') with nowait
        goto skip_sp__fn
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__fn') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__fn'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__fn]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090707.1055\s.zaglio: revision
    v:090630.1055\s.zaglio: utility per chiamare velocemente le FN_...
    t:sp__fn ''address''
*/
CREATE proc [dbo].[sp__fn] @name sysname=null,
    @v1 sql_variant=null,@v2 sql_variant=null,@v3 sql_variant=null
as
begin
set nocount on
declare @sql nvarchar(4000),@ename sysname,@msg nvarchar(4000)
declare @p table (id int identity, param sysname)
declare @i int,@n int,@params nvarchar(4000),@param sysname
if @name is null goto help
-- select xtype from sysobjects group by xtype
select @ename=name from sysobjects where name like @name and xtype in (''TF'',''IF'',''FN'')
if @ename is null select @ename=name from sysobjects where name like ''%''+@name+''%'' and xtype in (''TF'',''IF'',''FN'')
if @ename is null goto err_name

exec sp__usage @ename
insert into @p(param) select name from syscolumns where id=object_id(@ename) and left(name,1)=''@'' order by colid
select @params='''',@i=min(id),@n=max(id) from @p
while (@i<=@n) select  @params=@params+''null''+case when @i<@n then '','' else '''' end,@i=@i+1
set @sql=''select * from ''+@ename+''(''+@params+'')''
select lower(@sql) as cmd_line
exec(@sql)
goto ret

err_name:   select @msg=''function name not found''   goto ret

help:
exec sp__printf ''specificare nome o contenuto funzione''
exec sp__printf ''nb:procedura da ultimare''
ret:
if not @msg is null exec sp__printf @msg
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__fn:

-- ================================================================== sp__format
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__format',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130624
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__format') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__format') with nowait
        goto skip_sp__format
        end
    if @ver>130624
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__format') with nowait
        goto skip_sp__format
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__format') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__format'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__format]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    k:format,test
    g:utility
    v:130624\s.zaglio: added 0<3 into test to allow out usage
    v:130510\s.zaglio: added sample for 0<n
    v:130423\s.zaglio: added output to #tests
    v:130329\s.zaglio: added sample for $<
    v:121122\s.zaglio: better help
    v:120821\s.zaglio: added ''DD/MM/YYYY HH:MM:SS''
    v:120809\s.zaglio: help and test for fn__format
    t:declare @d datetime select @d=getdate() exec sp__format @d, ''hhmm'', 4
*/
CREATE proc sp__format
    @val sql_variant = null out,
    @fmt nvarchar(128) = null,
    @len int = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare
    @proc sysname, @err int, @ret int -- @ret: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare @id int,@d datetime,@drop bit
-- drop table #tests
if object_id(''tempdb..#tests'') is null
    begin
    create table #tests(
        id int identity,
        val sql_variant,
        fmt nvarchar(128),
        ln int,
        expected nvarchar(4000),
        comment nvarchar(4000),
        result nvarchar(4000)
        )
    select @drop=1
    end
else
    select @drop=0
-- =========================================================== initialization ==
select @d=''2012-08-21T16:28:11.000''
truncate table #tests
insert #tests select @d, ''hhmm'', 4, ''???'', null, null
insert #tests select @d, ''yyyy'', null, year(@d), null, null
insert #tests select ''0930'', ''hhmm'', 4, ''0930'', null, null
insert #tests select @d, ''HHMMSS'', 6, ''162811'', null, null
insert #tests select @d, ''DD/MM/YYYY HH:MM:SS'', null, ''21/08/2012 16:28:11'', null, null
insert #tests select @d, ''YYYYMMDD_HHMMSS'',null, ''20120821_162811'', null, null
insert #tests select @d, ''YYYYMMDDHHMMSS'',null, ''20120821162811'', null, null
insert #tests select ''strip.n0t''''AN: ;_chrs'', ''AN'', null, ''strip_n0t_AN____chrs'', ''normalize light'', null
insert #tests select ''..strip.n0t''''AN: ;_chrs..'', ''ANs'', null, ''strip_n0t_AN_chrs'', ''normalize heavy'', null
insert #tests select 123, ''0<'', 10, ''0000000123'', ''right padding with 0'', null
insert #tests select 123.435, ''0<2'', 10, ''0000012344'', ''right padding with 0 and round and decimals'', null
insert #tests select 123.435, ''0<3'', 10, ''0000123435'', ''right padding with 0 and round and decimals'', null
insert #tests select 123, ''$<'', 10, ''0000123.00'', ''right money padding with 0'', null
insert #tests select ''fn__format'', ''=<'', 20, ''==========fn__format'', ''right padding with ='', null
insert #tests select ''fn__format'', ''=< '', 20, ''========= fn__format'', ''right padding with = and a char before the name'', null
insert #tests select ''1239'', ''[eng]'', null, ''One thousand two hundred thirty-nine'', null, null
insert #tests select 1.104e+006, ''0<'', 10, ''0001104000'', ''right padding with 0'', null
insert #tests select ''aàsòì°fd'', ''^'', null, ''aa''''so''''i''''.fd'', ''transform accent'', null
insert #tests select ''test'', ''^'', null, ''test'', null, null
insert #tests select ''01021972'', ''@5678-34-12'', null, ''1972-02-01'',''relocate chars positions'', null
insert #tests select ''1234567890AB...'', ''@edcba0987654321'', null, ''...BA0987654321'',''relocate chars positions'', null

update #tests set result=cast(dbo.fn__format(val,fmt,ln) as nvarchar(4000))

--insert #tests select ''REP05_DELIVERY'', ''|HIS*'', null, ''HIS05_DELIVERY''
-- ======================================================== second params chk ==
if (@drop=0 or @val is null or @fmt is null) -- and @opt=''||''
    goto help

-- ===================================================================== body ==

print dbo.fn__format(@val,@fmt,@len)

goto ret

-- =================================================================== errors ==
/*
err_sample1:
exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param
goto ret
*/
-- ===================================================================== help ==
help:
if @drop=0 goto skip
exec sp__usage @proc,''
Scope
    give help about fn__format and test backward compatibility

Notes
    formats covered by CONVERT like YYYYMMDD(112),YYMMDD(12) are not covered.

Parameters
    #tests  optional table where returns tests
            create table #tests(
                id int identity,
                val sql_variant,
                fmt nvarchar(128),
                ln int,
                expected nvarchar(4000),
                comment nvarchar(4000),
                result nvarchar(4000)
                )
    @val    is the value passed to be formatted and the result
    @fmt    is the format (see below)
    @len    depending on @fmt can be null or a generic length
    @opt    options(not used)

-- List of formats with example and expected results --
''

exec sp__select_astext ''
    select
        id
        ,val
        ,fmt,ln
        ,expected
        ,result
        ,comment
    from #tests
    order by id
''

skip:
select top 1 @id=id from #tests where result!=expected and expected!=''???''
if not @id is null
    exec @ret=sp__err ''wrong result from fn__format in test %d'',@proc,@p1=@id
else
    select @ret=-1

if @drop=1 drop table #tests
-- select * from #tests

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__format'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__format:

-- ===================================================================== sp__ftp
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__ftp',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140107
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__ftp') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__ftp') with nowait
        goto skip_sp__ftp
        end
    if @ver>140107
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__ftp') with nowait
        goto skip_sp__ftp
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__ftp') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__ftp'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__ftp]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:140107\s.zaglio: adaptation to 64bit OS
    v:130604\s.zaglio: small improvements near #files cmds
    v:130603\s.zaglio: adapted to fn__ftp_parse_lst
    v:120620\s.zaglio: added ms.svr.dir output->#files
    v:120618\s.zaglio: a bug near winscp date
    v:120503\s.zaglio: winscp automatic if hostkey specified
    v:120502\s.zaglio: added hostkey parameter,help and files correct list
    v:120427\s.zaglio: tested winscp and correct a bug near console output
    r:120426\s.zaglio: adding support for winscp
    v:120413\s.zaglio: adopted @ftpout
    v:120404\s.zaglio: a bug near cmd #files (do not modify #ftpcmd)
    v:120204\s.zaglio: added support to #files for non unix svr
    v:120111\s.zaglio: added listing to #files
    v:111122\s.zaglio: added error 550 (cd)
    v:110715.1050\s.zaglio: added @cmd
    v:110530\s.zaglio: commented gen of ftpout and added trace of not managed error "file not found"
    v:110527\s.zaglio: added result ok rename ok
    v:110512\s.zaglio: added tests and delete ok
    v:110330\s.zaglio: call ms ftp.exe and execute content of #ftpcmd
    t:sp__ftp @opt=''check''
*/
CREATE proc sp__ftp
    @login nvarchar(1024)=null,
    @cmds nvarchar(4000)=null,
    @opt sysname=null,
    @dbg int=0
as
begin
set nocount on
set language us_english -- this dont change caller language
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0),
       @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==
if @login is null
or (object_id(''tempdb..#ftpcmd'') is null and @dbg=0 and @cmds is null)
    goto help

-- ============================================================== declaration ==
declare
    @tmp nvarchar(1024),@crlf nvarchar(2),
    @svr sysname,@db  sysname,@uid sysname,@pwd sysname,
    @ftp_svr sysname,@ftp_uid sysname,@ftp_pwd sysname,@ftp_path sysname,
    @ftp_key sysname,@winscp_path nvarchar(512),
    @i int,@ftp_cmd sysname,
    @file_path nvarchar(1024),@cmd nvarchar(4000),@file sysname,
    @files bit,@drop_files bit,@winscp bit,
    @pf nvarchar(512)                       -- path find


declare @ftpout table(lno int identity, line nvarchar(4000))
declare @ftp_msg table(code nvarchar(3))

if left(@cmds,6)=''#files''
    begin
    -- if @cmds results null, are used commands of #ftpcmd
    select @files=1,@cmds=''dir ''+nullif(substring(@cmds,8,128),'''')
    if object_id(''tempdb..#files'') is null
        begin
        select @drop_files=1
        create table #files (
            id int identity,
            rid int null,           -- for subdirs
            [flags] smallint,       -- if &32=32 is a <DIR>
            [key] nvarchar(256),    -- obj name
            dt datetime,            -- creation date
            n int null              -- size in bytes
            )
        end
    else
        select @drop_files=0
    end -- files cmd
else
    select @files=0

-- ok msg: do not return error
insert @ftp_msg(code)
        select ''150''    -- Opening ASCII mode data connection for file list.
union   select ''200''    -- command ok
union   select ''220''    -- welcome or identification "Microsoft FTP Service"
union   select ''226''    -- Transfer complete.
union   select ''221''    -- quitted
union   select ''230''    -- User XXXX logged in.
union   select ''250''    -- file deleted succesfully
union   select ''331''    -- Password required for wcarevivisol-mi3.
union   select ''350''    -- File exists, ready for destination name. (rename)


create table #ftpsrc(lno int identity, line nvarchar(4000))

-- =========================================================== initialization ==
select
    @crlf=crlf,
    @winscp = charindex(''|winscp|'',@opt)
from fn__sym()

select
    @ftp_svr=dbo.fn__str_at(@login,''|'',1),
    @ftp_uid=dbo.fn__str_at(@login,''|'',2),
    @ftp_pwd=dbo.fn__str_at(@login,''|'',3),
    @ftp_path=dbo.fn__str_at(@login,''|'',4),
    @ftp_key=dbo.fn__str_at(@login,''|'',5)

-- ======================================================== second params chk ==
if coalesce(@ftp_key,'''')!='''' select @winscp=1

if @winscp=1
    begin
    select @winscp_path=''winscp\winscp.com''
    exec sp__os_whereis @winscp_path out
    if @winscp_path is null goto err_wnf -- winscp not found
    end

-- ===================================================================== body ==

-- write the command file
exec sp__get_temp_dir @tmp out

select @file=''tmp_''+replace(convert(sysname,newid()),''-'',''_'')+''.txt''
select @file_path=@tmp+''\''+@file

if @dbg>0
    exec sp__printf ''svr:%s\nuid:%s\npwd:%s\npath:%s\nkey:%s'',
                    @ftp_svr,@ftp_uid,@ftp_pwd,@ftp_path,@ftp_key

-- ftp login commands
if @winscp=0
    begin
    insert #ftpsrc(line) values(''open ''+@ftp_svr)
    insert #ftpsrc(line) values(@ftp_uid)
    insert #ftpsrc(line) values(@ftp_pwd)
    end
else
    insert #ftpsrc(line)
    select ''open ''+@ftp_uid+'':''+@ftp_pwd+''@''+@ftp_svr+
           case
           when coalesce(@ftp_key,'''')!=''''
           then '' -hostkey="''+@ftp_key+''"''
           else ''''
           end

--insert #ftpsrc(line) values(''prompt'')
insert #ftpsrc(line) values(''binary'')
if coalesce(@ftp_path,'''')!='''' insert #ftpsrc(line) values(''cd "''+@ftp_path+''"'')
insert #ftpsrc(line) values(''lcd "''+@tmp+''"'')

if not object_id(''tempdb..#ftpcmd'') is null
and (@files=0 or @cmds is null)
    insert #ftpsrc(line) select line from #ftpcmd order by lno

if not @cmds is null
    begin
    if @files=1
        insert #ftpsrc(line) select @cmds
    else
        insert #ftpsrc(line)
        select token
        from fn__str_table(@cmds,''\\n'')
        order by pos
    end

if @dbg=2
    begin
    insert #ftpsrc(line) values(''put "''+@file_path+''"'')
    insert #ftpsrc(line) values(''ls'')
    insert #ftpsrc(line) values(''delete "''+@file+''"'')
    end
insert #ftpsrc(line) values(''bye'')

exec sp__file_write @file_path,@table=''#ftpsrc'',@addcrlf=1

/*  120503\s.zaglio:
if @dbg>0
    begin
    select @cmd=''dir "%s" & type "%s"''
    exec sp__str_replace @cmd out,''%s'',@file_path
    exec master..xp_cmdshell @cmd
    end
*/

-- now ftp
-- if @dbg=1
if @winscp=0
    select @cmd=''ftp -v -d -s:"%file%" ''-- +@ftp_svr
else
    select @cmd=@winscp_path+'' /script="%file%"''
-- else
--    select @cmd=''ftp -v -s:"%file%" %svr%''

exec sp__str_replace @cmd out,''%file%|%svr%'',@file_path,@ftp_svr

if @dbg>0 exec sp__printf ''cmd:%s'',@cmd

-- ##########################
-- ##
-- ## call external ftp util
-- ##
-- ########################################################
insert @ftpout(line) exec @ret=master..xp_cmdshell @cmd

-- it looks like ftp client(or iis server) add a #13 at the end of each line so i simply drop all #13
update @ftpout set line=replace(rtrim(line),char(13),'''')

-- manage not managed errors
if exists(
    select null from @ftpout
    where (lno>@i or @i is null)
    and (line like ''%connection refused''
        or line like ''unknown host%''
        or line like ''host sconosciuto%''
        or line like ''accesso non riuscito%''
        or line like ''invalid command%''
        or line like ''comando non valido''
        or line like ''%file not found''
        or line like ''%file non trovato''
        )
    )
    select @ret=-3

if exists(
    select null
    from @ftpout
    where 1=1
    and isnumeric(left(line,3))=1 and substring(line,4,1)='' '' -- XXX_
    and not left(line,3) in (select code from @ftp_msg)
    )
    begin
    -- select * from ftpout
    /* 110530\s.zaglio:
    if object_id(''ftpout'') is null
        create table ftpout(lno int, code nvarchar(3),err bit,line nvarchar(4000))
    insert ftpout
    select lno,left(line,3) code,case when left(line,3) in (select code from @ftp_msg) then 1 else 0 end as err,line
    from #ftpout
    */
    select @ret=-2
    end

if @dbg>0
    begin
    select
        lno,
        case when
            isnumeric(left(line,3))=1 and substring(line,4,1)='' '' -- XXX_
            and not left(line,3) in (select code from @ftp_msg)
        then ''***''
        else null
        end as err,
        line
    into #ftpoutdbg
    from @ftpout
    order by lno
    exec sp__select_astext ''select * from #ftpoutdbg order by lno'',@header=1
    drop table #ftpoutdbg
    end

if @ret=0 and @files=1
    begin
    declare @lng sysname
    select @lng=@@language
    set language english -- for jan, ...
    insert #files([key],flags,dt,n)
    select [name],case dir when ''d'' then 32 else 0 end,
           convert(datetime,[timestamp],100),size
    from @ftpout f
    cross apply fn__ftp_parse_list(f.lno,f.line,default)
    set language @lng
    end -- files

-- remove temp
-- if @dbg>0 goto skip_del      -- enable this only temporary because leave public tmp
select @cmd=''del "''+@file_path+''"''
exec master..xp_cmdshell @cmd,no_output
skip_del:

if @files=1 and (@drop_files=1 or @dbg=1)
    begin
    if @dbg=1 exec sp__select_astext ''select * from #files'',@header=1
    if @drop_files=1
        begin
        select * from #files
        drop table #files
        end
    end

if @dbg>0
    exec sp__select_astext ''select * from #ftpsrc order by lno'',@header=1

if object_id(''tempdb..#ftpout'') is null
    select line from @ftpout order by lno
else
    insert #ftpout(line) select line from @ftpout order by lno

drop table #ftpsrc

goto ret

-- =================================================================== errors ==

err_wnf: exec @ret=sp__err ''WINSCP not found'',@proc; goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    wrapper for MS ftp.exe and execute content of #ftpcmd
    and return results into #ftpout (if exists or are printed)

Parameters
    @login      ftp_svr|uid|pwd|[ftp_path]|[hostkey]

    @cmds       test or fast command (can be multiple if separated by \\n)
                Accept special command for files listing
                #files     or      #files:pattern     (use *)
                if present, stores results into
                    create table #files (
                        id int identity,
                        rid int null,           -- for subdirs
                        [flags] smallint,       -- if &32=32 is a <DIR>
                        [key] nvarchar(256),    -- obj name
                        dt datetime,            -- creation date
                        n int null              -- size in bytes
                        )
                (see sp__dir)

    @opt        options     description
                winscp      force use of winscp,
                            automatic if hostkey is specified

    @dbg        1=test connection and show dbg info
                2=test upload list and delete
    #ftpcmd     list of ftp commands to execute
    #ftpout     (optional) append the output to this (keeping original data)
    return      0 if ok, -1 if help, -2 if a non ok replies
                ok replies are
                200 type set to I
                220 Service ready for new user.
                221 Service closing control connection.
                    Logged out if appropriate.
                230 User logged in, proceed.
                250 file deleted succesfully
                331 User name okay, need password.
                350 File exists, ready for destination name. (rename)
                550 failed change directory (cd)

                For all others see http://www.ietf.org/rfc/rfc2821.txt

Notes
    create table #ftpcmd(lno int identity,line nvarchar(4000))
    create table #ftpout(lno int identity,line nvarchar(4000))

Examples
    sp__ftp "10.0.0.2|uid|pwd","ls"
    sp__ftp "10.0.0.1|uid|pwd||ssh-rsa 2048 5c:a4:...","ls",@opt="winscp"

''

select @winscp_path=''winscp\winscp.com''
exec sp__os_whereis @winscp_path out
if not @winscp_path is null exec sp__printf ''-- winscp path: %s'',@winscp_path
select @winscp_path=''ftp.exe''
exec sp__os_whereis @winscp_path out
if not @winscp_path is null exec sp__printf ''-- ftp path: %s'',@winscp_path

select @ret=-1

-- ===================================================================== exit ==
ret:
if @dbg>0 exec sp__printf ''return: %d'',@ret
return @ret

end -- proc sp__ftp'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__ftp:

-- ============================================================== sp__ftp_manage
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__ftp_manage',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130628
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__ftp_manage') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__ftp_manage') with nowait
        goto skip_sp__ftp_manage
        end
    if @ver>130628
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__ftp_manage') with nowait
        goto skip_sp__ftp_manage
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__ftp_manage') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__ftp_manage'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__ftp_manage]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:ftp,manage,download,upload,delete,rename,ok,err
    v:130628\s.zaglio: bug near path
    v:130626\s.zaglio: bug near rename
    r:130614\s.zaglio: refined
    r:130302\s.zaglio: common ftp functionality
*/
create proc sp__ftp_manage
    @login nvarchar(1024) = null,
    @path nvarchar(1024) = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @temp nvarchar(512),@cmd nvarchar(4000),
    @files bit,@ftpcmd bit,@ftpout bit, -- if relative table exists
    @flg_download smallint,@flg_upload smallint,
    @flg_delete smallint,@flg_ok smallint, @flg_err smallint,
    @end_declare bit

-- =========================================================== initialization ==
select
    @path=nullif(@path,''''),
    @files=isnull(object_id(''tempdb..#files''),0),
    @ftpout=isnull(object_id(''tempdb..#ftpout''),0),
    @ftpcmd=isnull(object_id(''tempdb..#ftpcmd''),0),
    @flg_download=[files.download],
    @flg_upload=[files.upload],
    @flg_delete=[files.delete],
    @flg_ok=[files.ok],
    @flg_err=[files.err],
    @end_declare=1
from flags

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if @login is null or @files=0 goto help

-- =============================================================== #tbls init ==
if @ftpcmd=0
    create table #ftpcmd(lno int identity primary key,line nvarchar(4000))
if @ftpout=0
    create table #ftpout(lno int identity primary key,line nvarchar(4000))

-- ===================================================================== body ==
if charindex(''%temp%'',@path)>0 -- if null invalidate all statement
    begin
    exec sp__get_temp_dir @temp out
    select @path=replace(@path,''%temp%'',@temp)
    end

if @path!=''''
    begin
    insert #ftpcmd select ''lcd "''+@path+''"''

    -- if nothing to upload, drop and create temp path
    if not exists(select top 1 null from #files where flags&@flg_upload!=0)
        begin
        select @cmd=''del /s/q/f ''+@path+
                    ''&rmdir /s/q ''+@path+
                    ''&mkdir ''+@path
        exec xp_cmdshell @cmd,no_output
        end
    end

-- default order: download, delete, rename, upload
insert #ftpcmd
select ''get "''+[key]+''"''
from #files
where flags&@flg_download!=0

insert #ftpcmd
select ''del "''+[key]+''"''
from #files
where flags&@flg_delete!=0

insert #ftpcmd
select ''ren "''+[key]+''" "''+[key]+
       case
       when flags&@flg_ok!=0 then ''.ok''
       when flags&@flg_err!=0 then ''.err''
       end+''"''
from #files
where flags&(@flg_ok|@flg_err)!=0

-- upload as temp files
insert #ftpcmd
select ''put "''+[key]+''" "tmp_''+[key]+''.tmp"''
from #files
where flags&@flg_upload!=0

-- rename uploaded temp
insert #ftpcmd
select ''ren "''+[key]+''" "tmp_''+[key]+''.tmp" "''+[key]+''"''
from #files
where flags&@flg_upload!=0

if @dbg in (0,2)
    begin
    exec @ret=sp__ftp @login
    if @dbg>1 exec sp__print_table ''#ftpout''
    end
else
    exec sp__print_table ''#ftpcmd''

-- ================================================================== dispose ==
dispose:
if @ftpcmd=0 drop table #ftpcmd
if @ftpout=0 drop table #ftpout

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    manage common ftp functionality as upload,download,rename,delete,etc.

Parameters
    #files      list of files according to sp__ftp
                settings flags field with flags.[files.*] we can simply
                manage files
    #ftpcmd     optional (caller must manage the fill/unfill)
    #ftpout     optional (caller must manage the cleaning)
    @login      login info according to sp__ftp @login
    @path       optional path where found files to download/upload
    @opt        options (not used)
    @dbg        1=show ftp cmds without execute it
                2=execute ftp cmds and show ftp out

Examples
    create table #files ....
    exec sp__ftp "127.0.0.1|usr|pwd","#files:*"
    update #files set flags=flags|flg.[files.del] from flags
    exec sp__ftp_manage "127.0.0.1|usr|pwd"     -- this will delete all files
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__ftp_manage'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__ftp_manage:

-- ================================================================= sp__geocode
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__geocode',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120124
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__geocode') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__geocode') with nowait
        goto skip_sp__geocode
        end
    if @ver>120124
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__geocode') with nowait
        goto skip_sp__geocode
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__geocode') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__geocode'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__geocode]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:gps,coords,street
    v:120124\s.zaglio: ask google for coords
    c:origin http://www.sqlservercentral.com/articles/geocode/70061/
*/
CREATE proc sp__geocode
    @opt sysname = null,
    @dbg int = null
as
begin

-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if object_id(''tempdb..#addr'') is null goto help

-- ============================================================== declaration ==

declare
    @address varchar(80) ,
    @city    varchar(40) ,
    @state   varchar(40) ,

    @country varchar(40) ,
    @postalcode varchar(20),

    @county varchar(40) ,

    @gpslatitude numeric(9,6) ,
    @gpslongitude numeric(9,6),
    @mapurl varchar(1024)

declare
    @url varchar(max),
    @response varchar(8000),
    @xml xml,
    @obj int ,
    @hr int ,
    @httpstatus int ,
    @error varchar(max),
    @tmp nvarchar(4000)

-- =========================================================== initialization ==

-- ======================================================== second params chk ==
-- ===================================================================== body ==

-- ##########################
-- ##
-- ## be carefull with upcase letters on xml path
-- ##
-- ########################################################

exec @hr = sp_oacreate ''MSXML2.ServerXMLHttp'', @obj out
if @hr!=0 goto err_ole

declare cs cursor local for
    select
        gpslatitude,
        gpslongitude,
        city,
        [state] ,
        postalcode ,
        [address],
        country ,
        county,
        mapurl ,
        error
    from #addr
    where 1=1
open cs
while 1=1
    begin
    fetch next from cs into
        @gpslatitude,
        @gpslongitude,
        @city,
        @state,
        @postalcode,
        @address,
        @country,
        @county,
        @mapurl,
        @error

    if @@fetch_status!=0 break

    exec sp__printf ''addr:%s, state:%s'',@address,@state

    select @url =
        ''http://maps.google.com/maps/api/geocode/xml?sensor=false&address='' +
        case when @address is not null then @address else '''' end +
        case when @city is not null then '', '' + @city else '''' end +
        case when @state is not null then '', '' + @state else '''' end +
        case when @postalcode is not null then '', '' + @postalcode else '''' end +
        case when @country is not null then '', '' + @country else '''' end
    select @url = replace(@url, '' '', ''+'')
    if @dbg=1
        begin
        select @tmp=cast(@url as nvarchar(4000))
        exec sp__printf ''get url:%s'',@tmp
        end

    exec @hr = sp_oamethod @obj, ''open'', null, ''GET'', @url, false
    if @hr!=0 goto err

    exec @hr = sp_oamethod @obj, ''setRequestHeader'', null,
                                 ''content-type'',
                                 ''application/x-www-form-urlencoded''
    if @hr!=0 goto err

    exec @hr = sp_oamethod @obj, ''send'', null, ''''
    if @hr!=0 goto err

    select @httpstatus=null,@response=null,@xml=null
    exec @hr = sp_oagetproperty @obj, ''status'', @httpstatus out
    if @hr!=0 goto err
    -- exec @hr = sp_oagetproperty @obj, ''responseXML.xml'', @response out
    exec @hr = sp_oagetproperty @obj, ''responseText'', @response out

    err:
    if (@error is not null) or (@httpstatus <> 200) or (@hr!=0)
        begin
        exec sp_oageterrorinfo @obj, @error out, @tmp out
        if @httpstatus <> 200 and @hr=0
            select
                @error = ''error in spgeocode: '' +
                isnull(@error, ''http result is: '' +
                cast(@httpstatus as varchar(10)))
        else
            select @error=cast(@hr as sysname)+'':''+isnull(@error,''?'')+'' - ''+isnull(@tmp,''?'')
        goto skip
        end

    select @xml = cast(@response as xml)

    if @dbg>1
        begin
        select @tmp=cast(@xml as nvarchar(4000))
        exec sp__printf ''xml:%s'',@tmp
        end

    select
        @gpslatitude = @xml.value(''(/GeocodeResponse/result/geometry/location/lat) [1]'', ''numeric(9,6)''),
        @gpslongitude = @xml.value(''(/GeocodeResponse/result/geometry/location/lng) [1]'', ''numeric(9,6)''),
        @city = @xml.value(''(/GeocodeResponse/result/address_component[type="locality"]/long_name) [1]'', ''varchar(40)''),
        @state = @xml.value(''(/GeocodeResponse/result/address_component[type="administrative_area_level_1"]/short_name) [1]'', ''varchar(40)''),
        @postalcode = @XML.value(''(/GeocodeResponse/result/address_component[type="postal_code"]/long_name) [1]'', ''varchar(20)'') ,
        @country = @XML.value(''(/GeocodeResponse/result/address_component[type="country"]/short_name) [1]'', ''varchar(40)'') ,
        @county = @XML.value(''(/GeocodeResponse/result/address_component[type="administrative_area_level_2"]/short_name) [1]'', ''varchar(40)'') ,
        @address =
            ISNULL(@XML.value(''(/GeocodeResponse/result/address_component[type="street_number"]/long_name) [1]'', ''varchar(40)''), ''???'') + '' '' +
            ISNULL(@XML.value(''(/GeocodeResponse/result/address_component[type="route"]/long_name) [1]'', ''varchar(40)''), ''???''),
        @mapurl = ''http://maps.google.com/maps?f=q&hl=en&q='' + cast(@gpslatitude as varchar(20)) + ''+'' + cast(@gpslongitude as varchar(20))

    skip:
    update #addr set
        gpslatitude     =@gpslatitude,
        gpslongitude    =@gpslongitude,
        city            =@city,
        [state]         =@state,
        postalcode      =@postalcode,
        [address]       =@address,
        country         =@country,
        county          =@county,
        mapurl          =@mapurl,
        error           =@error
    where current of cs

    end -- while of cursor
close cs
deallocate cs

exec @hr = sp_oadestroy @obj

goto ret

-- =================================================================== errors ==
err_ole:    exec @ret=sp__err ''object creation error'',@proc goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    fill a table with corrected addresses and coords from google maps

Parameters
    #addr   is the table with addresses. Must be specified at least
            address,city and state

            create table #addr(
                [address]   varchar(80) null,
                city        varchar(40) null,
                [state]     varchar(40) null,
                country     varchar(40) null,
                postalcode  varchar(20) null,
                county      varchar(40) null,
                gpslatitude numeric(9,6) null,
                gpslongitude numeric(9,6) null,
                mapurl      varchar(1024) null,
                error       varchar(max) null
            )

Examples
    insert #addr(address,city,state)
    select "1234 N. Main Street","Sant Ana","CA" union
    select "219 E Washington Ave","Sant Ana","CA" union
    select "219 E Washington Ave",null,"CA"     -- this cause an error
                                                -- but the url would work
    exec sp__geocode
    select * from #addr
''

-- ===================================================================== exit ==
ret:
return @ret
end -- sp__geocode'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__geocode:

-- ================================================================= sp__get_env
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__get_env',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__get_env') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__get_env') with nowait
        goto skip_sp__get_env
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__get_env') with nowait
        goto skip_sp__get_env
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__get_env') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__get_env'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__get_env]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091018\s.zaglio: added help
    v:090910\s.zaglio: get os env var
    t:declare @tmp nvarchar(128) exec sp__get_env @tmp out,''allusersprofile'' print @tmp
*/
CREATE proc [dbo].[sp__get_env]
    @val sysname=null output,
    @var sysname=null
as
begin
set nocount on
create table #lines (lno int identity, line nvarchar(4000))

declare @cmd nvarchar(4000), @crlf nchar(2)
set @cmd=''set''
set @val=null

insert #lines (line)
exec master.dbo.xp_cmdshell @cmd--,no_output -- if use this don''t work
if @var is null select * from #lines
else
    select @val=isnull(rtrim(substring(line,charindex(''='',line)+1,256)),'''')
    from #lines
    where line like @var+''=%''

drop table #lines
if @var is null exec sp__usage ''sp__get_env''
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__get_env:

-- ============================================================ sp__get_temp_dir
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__get_temp_dir',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131208
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__get_temp_dir') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__get_temp_dir') with nowait
        goto skip_sp__get_temp_dir
        end
    if @ver>131208
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__get_temp_dir') with nowait
        goto skip_sp__get_temp_dir
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__get_temp_dir') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__get_temp_dir'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__get_temp_dir]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    d:131208\s.zaglio:sp__get_tmp
    v:131208\s.zaglio:added opt TF and TEST
    v:130830.1000\s.zaglio:remove final \ if exists and added help
    v:100402\s.zaglio:returned dir is without final \\

    v:090910\s.zaglio:adapted to use sp__get_env
    v:080414\s.zaglio:creation
    t:sp__get_temp_dir @opt=''tf|test''
*/
CREATE proc [dbo].[sp__get_temp_dir]
    @dir nvarchar(512) = null output,
    @opt sysname = null
as
begin
set nocount on
declare @tf bit,@test bit
if not @opt is null
    begin
    select @opt=''|''+@opt+''|''
    select @tf=charindex(''|tf|'',@opt),@test=charindex(''|test|'',@opt)
    end
else
    select @tf=0,@test=0

exec sp__get_env @dir out,''temp''
if right(@dir,1)=''\'' select @dir=left(@dir,len(@dir)-1)
if @tf=1 select @dir=@dir+''\tmp_''+replace(convert(sysname,newid()),''-'',''_'')
if @test=0
and (@@nestlevel>1 -- in remote is 1 even if called by a local sp ...
or dbo.fn__isconsole()=0)    -- ... this solve the above problem
     return

if @test=1
    begin
    exec sp__printf ''%s'',@dir
    return
    end

declare @proc sysname
select @proc=object_name(@@procid)
exec sp__usage @proc,''
Scope
    return value of OS "temp" environment variable
    without last \.

Notes
    if called under a sp, this help will not shown

Parameters
    @dir    is the output var that will contain the path
    @opt    options
            tf      attach a temp file name from newid()
            test    print output

Example
    declare @tmp nvarchar(512)
    exec %proc% @tmp out
    print @tmp                      -- will return "%p1%"
'',@p1=@dir
end -- sp__get_temp_dir'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__get_temp_dir:

-- =================================================================== sp__getid
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__getid',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080817
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__getid') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__getid') with nowait
        goto skip_sp__getid
        end
    if @ver>080817
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__getid') with nowait
        goto skip_sp__getid
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__getid') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__getid'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__getid]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080817\S.Zaglio: complete alternativelly id or name
    t:
        declare @id int,@ido sysname
        select @ido=convert(sysname,object_id(''sysobjects''))
        exec sp__getid @ido out,''sysobjects'',''name'',''id'',@dbg=1
        if @ido is null print ''not found by id''
        else print @ido
        exec sp__getid @ido out,''sysobjects'',''name'',''id'',@id_only=0,@dbg=1
        if @ido is null print ''not found by id''
        else print @ido
        select @ido=''sysobjects''
        exec sp__getid @ido out,''sysobjects'',''name'',''id'',@dbg=1
        if @ido is null print ''not found by name''
        else print @ido
*/
CREATE proc sp__getid
    @param sysname=null out,
    @table sysname=null,
    @name sysname=null,
    @id sysname=null,
    @id_only bit=1,
    @dbg bit=0
as
begin
set nocount on
declare @r int
exec @r=sp__chknulls @param,@table,@name,@id
if @r=1 goto help

declare @sql nvarchar(4000)
if isnumeric(@param)=1
    begin -- verify anyway that exists
    if @dbg=1 and @id_only=1 print ''id->id''
    if @dbg=1 and @id_only=0 print ''id->name''
    if @id_only=1 select @sql=''select @id=convert(sysname,%id%) from %tbl% where %id%=convert(int,%param%)''
    else select @sql=''select @id=%name% from %tbl% where %id%=convert(int,%param%)''
    exec sp__str_replace @sql out,''%id%|%tbl%|%name%|%param%'',@id,@table,@name,@param
    exec sp_executesql @sql,N''@id sysname out'',@id=@param out
    end
else
    begin -- get id from name
    if @dbg=1 print ''name->id''
    select @sql=''select @id=convert(sysname,%id%) from %tbl% where %name%=''''%param%''''''
    exec sp__str_replace @sql out,''%id%|%tbl%|%name%|%param%'',@id,@table,@name,@param
    exec sp_executesql @sql,N''@id sysname out'',@id=@param out
    end

goto ret
help:
exec sp__usage ''sp__getid''
ret:
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__getid:

-- =================================================================== sp__group
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__group',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090507.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__group') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__group') with nowait
        goto skip_sp__group
        end
    if @ver>090507.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__group') with nowait
        goto skip_sp__group
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__group') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__group'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__group]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090507.1000\s.zaglio: stored of example
    t:sp__group ''utility'',''sp__group''
*/
CREATE proc sp__group
    @group sysname=null,
    @objs sysname=null, --- objs to add
    @order int=null,
    @parent sysname=null,
    @chk sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare @r int
raiserror(''todo and rethinking'',18,1)
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__group:

-- ==================================================================== sp__hkey
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__hkey',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130929
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__hkey') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__hkey') with nowait
        goto skip_sp__hkey
        end
    if @ver>130929
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__hkey') with nowait
        goto skip_sp__hkey
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__hkey') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__hkey'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__hkey]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:horizontal,keys,
    r:130929\s.zaglio: convert a list of keys in horizontal
    t:sp__hkey_test
*/
CREATE proc sp__hkey
    @column nvarchar(4000) = null,
    @hkey nvarchar(max) = null out,
    @opt sysname = null,
    @dbg int=0
as
begin try
set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==

declare @data nvarchar(max),@sep char,@nolist bit

-- =========================================================== initialization ==

select @column=nullif(@column,''''),@sep=''|'',@nolist=charindex(''|nolist|'',@opt)

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if @column is null goto help

-- =============================================================== #tbls init ==
create table #tmp(ukey sysname)
-- ===================================================================== body ==

select @column=''insert into #tmp select * from (''+@column+'') a''
exec sp__printf ''%s'',@column
exec(@column)

select @data=
 stuff(
    (select @sep + ukey
    from #tmp
    for xml path(''''), type
    ).value(''(./text())[1]'',''nvarchar(max)'')
  , 1, len(@sep), '''')

select @hkey=replace(@data,@sep,'''')

if @nolist=1 goto dispose
;with pieces(pos, start, [stop]) as (
  select
    cast(1 as int), cast(1 as int),
    charindex(@sep, @data)
  union all
  select
    cast(pos + 1 as int), cast([stop] + 1 as int),
    charindex(@sep, @data, [stop] + 1)
  from pieces
  where [stop] > 0
)
select -- row_number() over(order by (select 0)) row,
  pos,
  substring(@data, start,
            case when [stop] > 0 then [stop]-start else 4000 end
            ) as token,
  start,
  substring(@hkey,start-pos+1,10) ex
from pieces
option (maxrecursion 0)

-- ================================================================== dispose ==
dispose:
drop table #tmp

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    [write here a short desc]

Parameters
    [param]     [desc]
    @column     sql select of column key
    @opt        options
                nolist      do not output list os positions
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    sp__hkey "select ukey from table"
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"
exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__hkey'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__hkey:

-- ==================================================================== sp__info
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__info',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131107.1200
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__info') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__info') with nowait
        goto skip_sp__info
        end
    if @ver>131107.1200
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__info') with nowait
        goto skip_sp__info
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__info') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__info'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__info]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131107.1200\s.zaglio: removed list of objs when one found and about sys... objs
    v:131001,130906\s.zaglio: added shortcut info
    v:110926\s.zaglio: added sys management
    v:110916\s.zaglio: used readpast
    v:110706\s.zaglio: special behaviour on systypes
    v:110629\s.zaglio: added list of triggers
    v:110628\s.zaglio: added db trigger script
    v:110623\s.zaglio: added call of sp__style
    v:110406\s.zaglio: added script of triggers
    v:110307.1000\s.zaglio: a small bug on single sp
    v:110220\s.zaglio: added system tables
    v:110213\s.zaglio: replaced readpast with nolock
    v:110102\s.zaglio: added obj info
    v:110121\s.zaglio: added nolock
    v:101107\s.zaglio: added show of structure of temp tables
    v:100919\s.zaglio: added show of ##var## and #var#
    v:100616\s.zaglio: removed help after attached
    v:100529\s.zaglio: added use of #dbg
    v:100528\s.zaglio: a bug near local srv, other db obj position
    v:100518\s.zaglio: added temp vars to not disturbe scope of caller
    v:100423\s.zaglio: manage #tbls
    v:100422\s.zaglio: a minor enh
    v:100418\s.zaglio: added synonym management
    v:100228\s.zaglio: help developers;set this into tools\options menuù and connecto to CTRL+F1
    t:sp__info #test1_2#3   -- good
    t:sp__info ''sysobjects''
    t:fn__str, sp__emails, tr__script_trace_db
    t:sp__info ''sp__info''
    t:sp__info ''sp__server_ip''
*/
CREATE proc [sp__info] @what sysname=null
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0
declare @dbg bit select @dbg=0
declare @crlf nvarchar(2)

select @crlf=crlf from dbo.fn__sym()
-- cerate local temps
create table #src(lno int identity, line nvarchar(4000))
create table #vars (id nvarchar(16),value sql_variant)

if not object_id(''tempdb..#dbg'') is null
    begin
    if @what is null
        begin
        exec(''#dbg'')
        goto ret
        end
    exec sp_executesql N''exec @ret=#dbg @what'',
                       N''@ret int out,@what sysname'',
                       @ret=@ret out,@what=@what
    if @dbg=-1 exec sp__printf ''#dbg:%d'',@ret
    if @ret!=0 goto ret
    end

if not object_id(''sp_info'') is null
    begin
    if @what is null begin exec(''sp_info'') goto ret end
    exec @ret=sp_executesql
                N''@ret=sp_info @what'',
                N''int @ret out,@what sysname'',
                @ret=@ret out,@what=@what
    if @dbg=-11 exec sp__printf ''sp_info:%d'',@ret
    if @ret!=0 goto ret
    end

if @what is null goto help

if @dbg=1 exec sp__printf ''--- analyze''

declare
    @obj sysname,@type sysname,@objs nvarchar(4000),
    @n int,@i int,@sql nvarchar(4000),@id int,
    @svr sysname,@db sysname,@sch sysname,@sobj sysname,
    @synonym bit,@ok bit

select @ok=0

if left(@what,1)=''#''
and dbo.fn__str_count(@what,''#'')>=3
and right(@what,1)!=''#''
    begin
    select @what=substring(replace(@what,''_'','' ''),2,4000)
    exec sp__style @what
    goto ret
    end

if left(@what,2)=''##'' and right(@what,2)=''##''
    begin
    if @dbg=1 exec sp__printf ''--- ##cfg''
    -- ##smtp_server##
    select @sql=N''sp__config ''''''+substring(@what,3,len(@what)-4)+''''''''
    exec(@sql)
    end
else
    begin
    if left(@what,1)=''#'' and right(@what,1)=''#''
        begin
        if @dbg=1 exec sp__printf ''--- #cfg''
        select @sql=N''sp_config ''''''+substring(@what,2,len(@what)-2)+''''''''
        exec(@sql)
        end
    end

select @id=object_id(@what)
if @id is null and dbo.fn__ismssql2k()=0
    select @id=object_id,@type=[type]
    from sys.triggers
    where [name]=@what
if @dbg=1 exec sp__printf ''@id=%d'',@id
if @id is null and left(@what,1)=''#'' and right(@what,1)!=''#''
    begin
    if @dbg=1 exec sp__printf ''--- direct id''

    select @id=object_id(''tempdb..''+@what),@type=''u'' -- could be a #proc: todo
    select fld,def into #tmptbldef
    from fn__sql_def_cols(@what,default,default)
    exec sp__select_astext
        ''select fld,def from #tmptbldef with (nolock)'',
        @header=1
    drop table #tmptbldef
    end

select @objs=coalesce(@objs+''|'','''')+[name]+''(''+xtype+'')'',@type=xtype
from sysobjects with (nolock)
where [name] like replace(@what,''_'',''[_]'')+''%''
if dbo.fn__ismssql2k()=0
    select @objs=coalesce(@objs+''|'','''')+[name]+''(''+[type]+'')'',@type=[type]
    from sys.triggers with (nolock)
    where [name] like replace(@what,''_'',''[_]'')+''%''
    and parent_id=0

-- sp__info sp_
select @n=dbo.fn__str_count(@objs,''|'')
if @n>1 and @id is null
    begin
    if @dbg=1 exec sp__printf ''--- multi find''
    select @n=max(len(token))+1 from dbo.fn__str_table(@objs,''|'')
    select @sql=null
    declare @ll int select @ll=(132/@n*@n)+2
    -- select * from dbo.fn__str_table(''a,b,c'','','')
    select @sql =coalesce(@sql,'''')+left(token+replicate('' '',@n),@n)
                +case when pos%(132/@n)=0 then @crlf else ''  '' end
    from dbo.fn__str_table(@objs,''|'')
    print @sql
    print ''-------------------------------------------------------------------------''
    -- exec sp__printf ''%s'',@sql
    end

if not @id is null or @what like ''sys[0-9]%''
    begin
    if @dbg=1 exec sp__printf ''type is %s'',@type
    if @type=''sn''
        begin
        select @synonym=1
        select -- lower(sy.type_desc),sc.name + ''.'' + sy.name as synonym_name,sy.base_object_name,
            @sql=sy.base_object_name,
            @svr=parsename(sy.base_object_name,4),
            @db=parsename(sy.base_object_name,3),
            @sch=parsename(sy.base_object_name,2),
            @sobj=parsename(sy.base_object_name,1)
        from sys.synonyms sy with (nolock)
        join sys.schemas  sc with (nolock) on sc.schema_id = sy.schema_id
        where sy.[object_id] = @id

        exec sp__printf ''-- synonym: -> %s.%s.%s.%s'',@svr,@db,@sch,@sobj

        if @dbg=1 exec sp__printf ''%s sv:%s db:%s sc:%s ob:%s'',@sql,@svr,@db,@sch,@sobj
        select @sql=''use [%db%] select @type=xtype from sysobjects with (nolock) where id=object_id(''''[%sch%].[%obj%]'''')''
        exec sp__str_replace @sql out,''%svr%|%db%|%sch%|%obj%'',@svr,@db,@sch,@sobj
        select @sql=replace(@sql,'''''''','''''''''''')
        if @svr is null
            select
                @sql=''exec [%db%]..sp_executesql N''''''+@sql+'''''',N''''@type sysname out'''',@type=@type out'',
                @what=quotename(@db)+''.''+quotename(@sch)+''.''+quotename(@sobj)
        else
            select
                @sql=''exec [%svr%].[%db%]..sp_executesql N''''''+@sql+'''''',N''''@type sysname out'''',@type=@type out'',
                @what=quotename(@svr)+''.''+quotename(@db)+''.''+quotename(@sch)+''.''+quotename(@sobj)

        exec sp__str_replace @sql out,''%svr%|%db%|%sch%|%obj%'',@svr,@db,@sch,@sobj
        select @type=null
        exec sp_executesql @sql,N''@type sysname out'',@type=@type out
        if @dbg=1 exec sp__printf ''@sql:%s @type:%s @what:%s'',@sql,@type,@what
        end

    -- fn_mc_and_wh
    if @what like ''sys[^0-9]%''
    and not @what in (''systypes'',''sysobjects'',''syscolumns'',''syscomments'')
        select @what=''master..''+@what
    if @what like ''sys%''
        select @sql=''select * from ''+@what+'' with (nolock)''
    else
        select @sql=''select top 10 * from ''+@what+'' with (nolock)''

        /* in the 110922, readpast caused in production the msg:
            Messaggio 650, livello 16, stato 1, riga 1
            You can only specify the READPAST lock in the READ COMMITTED or REPEATABLE READ isolation levels.
        */
    -- select xtype from sysobjects group by xtype
    if @type in (''p'',''fn'',''if'',''tf'',''tr'')
        begin
        if @dbg=1 exec sp__printf ''--script''
        exec sp__script @what,@opt=''print''
        select @ok=1
        end
    if (@type in (''v'',''u'') and @id is null) or (@type is null and not @id is null)
        begin
        if @dbg=1 exec sp__printf ''-- show as text:\n%s'',@sql
        exec sp__select_astext @sql
        select @ok=1
        end
    if @type in (''v'',''u'') and not @id is null
        begin
        if @dbg=1 exec sp__printf ''-- show data:\n%s'',@sql
        exec(@sql)
        select @sql=quotename(@svr)+''.''+quotename(@db)+''.dbo.sp__script ''''''+@sobj+'''''',@opt=''''reverse|print''''''
        if @dbg=1 exec sp__printf ''%s'',@sql
        if @synonym=1 exec(@sql)
        if @dbg=1 exec sp__printf ''-- script''
        else exec sp__script @what,@opt=''print''            -- table or view
        select @ok=1
        end
    if @type is null and not @sql is null exec(@sql)
    end

else -- not @id is null

    begin
    if @dbg=1 exec sp__printf ''--- single wild found''
    if @objs is null -- try search with one char less
        select @objs=coalesce(@objs+''|'','''')+[name]+''(''+xtype+'')'',@type=xtype
        from sysobjects with (nolock)
        where [name] like replace(left(@what,len(@what)-1),''_'',''[_]'')+''%''

    exec sp__printf ''%s'',@objs
    goto ret
    end

    if @ok=1 goto ret

help:
exec sp__usage @proc,''
Scope
    Help developers printing some info

Notes
    I normally do this associations:
        ctrl+F1: sp__info
        ctrl+3:  connection info (see below)
        ctrl+4:  sp__dir @opt=''''*'''',@path=
        ctrl+5:  (free)
        ctrl+6:  sp__find
        ctrl+7:  declare @db sysname;select @db=db_name();exec sp_script @db=@db,@obj=
        ctrl+8:  sp__style
        ctrl+9:  sp__script_debug
        ctrl+0:  (free)

    Connection info (to put into single line before paste as shortcut):
        select
            @@servername svr,db_name() db,cn.connect_time,cn.client_net_address,cn.local_net_address
        from sys.dm_exec_connections cn
        where session_id=@@spid

    If exist into current db an SP_INFO, first are called this.
    If this return something !=0, stop execution otherwise continue showing info.

    Infos depends from parameter type:
    1. proc/function/trigger       show source
    2. view/table                  select first 10 records
    3. root of object              if find one, reapply 1 & 2
    3.1                            if find more, print list
    4. not 1,2,3                   use sp__find @what
    5. #...                        introduce a command
    5.1 #svn#sp__info#s_zaglio     call sp__svn... replace _ with . (TODO)
    5.2 ##var##                    call sp__config ''''var''''
    5.3 #var#                      call SP_CONFIG ''''var''''
''
exec sp__printf ''Parameter:%s (!!!! keep in mind "__")'',@what
select @ret=-1
goto ret

ret:
drop table #src
drop table #vars
return @ret
end -- sp__info'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__info:

-- =============================================================== sp__info_code
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__info_code',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140115
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__info_code') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__info_code') with nowait
        goto skip_sp__info_code
        end
    if @ver>140115
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__info_code') with nowait
        goto skip_sp__info_code
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__info_code') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__info_code'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__info_code]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:code,statistics,lines,per,x,day,period
    v:140115\s.zaglio: added help
    v:090906\s.zaglio: do some statistics about code
*/
CREATE proc sp__info_code
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @eol nvarchar(4),@ln int,
    @app_dd money,@app_lns money,@utl_dd money,@utl_lns money,
    @app_n int,@utl_n int

-- =========================================================== initialization ==
-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1
-- ======================================================== second params chk ==
-- =============================================================== #tbls init ==

-- ===================================================================== body ==

select @eol=char(13),@ln=len(@eol)

select @app_lns=sum((len(definition)-len(replace(definition,@eol,'''')))/@ln)
from sys.sql_modules m
where not object_name(object_id) like ''%[_][_]%''

select @utl_lns=sum((len(definition)-len(replace(definition,@eol,'''')))/@ln)
from sys.sql_modules m
where object_name(object_id) like ''%[_][_]%''

select @app_dd=sum(datediff(dd,create_date,modify_date)),@app_n=count(*)
-- select top 1 *
from sys.objects
join sys.sql_modules on sys.objects.object_id=sys.sql_modules.object_id
where not name like ''%[_][_]%''
and [type]!=''S''

select @utl_dd=sum(datediff(dd,create_date,modify_date)),@utl_n=count(*)
-- select top 1 *
from sys.objects
join sys.sql_modules on sys.objects.object_id=sys.sql_modules.object_id
where name like ''%[_][_]%''
and [type]!=''S''

select
    db_name() as db,
    @app_lns tot_app_lines,@app_dd tot_app_days,
    @app_lns/@app_dd app_lines_x_day,@app_n app_n_objs,
    cast(@app_lns/@app_n as int) app_lns_x_obj,
    @utl_lns tot_utl_lines,@utl_dd tot_utl_days,
    @utl_lns/@utl_dd utl_lines_x_day,@utl_n utl_n_objs,
    cast(@utl_lns/@utl_n as int) app_lns_x_obj,
    @utl_lns/@app_lns utl_lines_x_app

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    do some statistics about code (ecluding objects like table ofcourse)

Notes
    applications objects info (sp,fn,views, ecc that NOT likes "%__%"):
        tot_app_lines       total lines of code in the objects of application
        tot_app_days        sum of days from creation to last modifications of objs
        app_lines_x_day     average lines of code written per day
        app_n_objs          total number of objects of the application
        app_lns_x_obj       lines per object

    utilities objects info (sp,fn,views, ecc that likes "%__%"):
        tot_utl_lines
        tot_utl_days
        utl_lines_x_day
        utl_n_objs
        app_lns_x_obj

    utl_lines_x_app         relation between utilites and application

Parameters
    [param]     [desc]
    @opt        not used
    @dbg        not used

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__info_code'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__info_code:

-- ================================================================= sp__info_db
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__info_db',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130707
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__info_db') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__info_db') with nowait
        goto skip_sp__info_db
        end
    if @ver>130707
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__info_db') with nowait
        goto skip_sp__info_db
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__info_db') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__info_db'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__info_db]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    d:130630\s.zaglio: sp__maint_stats
    v:130707\s.zaglio: added multi pattern
    v:130630\s.zaglio: added rwstat option
    v:120910\s.zaglio: added density option
    v:120831\s.zaglio: added norm option
    v:120116\s.zaglio: added curr_idnt
    v:111216\s.zaglio: added index size in order
    v:100919.1005\s.zaglio: a remake
    t:
        select top 10 * into test from sysobjects
        exec sp__info_db ''test''
        -- exec sp_spaceused ''ORDRSP_EDK14''
        drop table test
    t:sp__info_db #,@dbg=1,@opt=''norm''
    t:sp__info_db ''cf%|ts%''
*/
CREATE proc [dbo].[sp__info_db]
    @objs nvarchar(512) = null,
    @opt  sysname = null,
    @dbg bit=0
as
begin
set nocount on

declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

-- declarations
declare
    @svr sysname,@db sysname,@sch sysname,@obj sysname,
    @qobj sysname,@id int,
    @sql nvarchar(max),@noout bit,@i int,@n int,
    @crlf varchar,
    @nodrop bit, @tbl_def nvarchar(4000)

select
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|''),
    @noout=1,@nodrop=0,
    @crlf=crlf
from fn__sym()

if @objs is null goto help

if @objs=''#'' select @objs=''%''

select @svr=svr,@db=db,@sch=sch,@obj=obj
-- select *
from dbo.fn__parsename(@objs,0,1)

if @svr!=dbo.fn__servername(null) goto err_lso

create table #likes(pattern sysname)
insert #likes select token from fn__str_table(@objs,''|'')

if object_id(''tempdb..#objs'') is null
    create table #objs(id int identity,obj sysname)
else
    select @nodrop=1

if object_id(''tempdb..#info_db'') is null
    begin
    create table #info_db (
        id int identity,

        [name] sysname,
        [rows] int          null,
        reserved sysname    null,
        data sysname        null,
        index_size sysname  null,
        unused sysname      null,

        max_row_len int     null,
        data_n int          null,
        index_size_n int    null,
        unused_n int        null,
        avg_bXrec real      null,
        ncols smallint      null,   -- number of columns
        curr_idnt bigint    null,   -- last/max identity value
        norm bigint         null,   -- size of normalized data

        useeks bigint       null,   -- whole index user seeks
        uscans bigint       null,   -- whole index user scans
        ulckps bigint       null,   -- whole index user lookups
    )
    end
else
    select @noout=0

set @sql=''
    use [%db%]
    insert into #objs(obj)
    select o.name
    from sysobjects o
    join #likes l on o.name like l.pattern
    where xtype=''''u''''
    ''
exec sp__str_replace @sql out,''%db%|%obj%'',@db,@obj

exec(@sql)

drop table #likes

select @i=min(id),@n=max(id) from #objs
while (@i<=@n)
    begin
    select @obj=quotename(@db)+''.''+quotename(@sch)+''.''+quotename(obj)
    from #objs where id=@i

    insert #info_db([name],[rows],reserved,data,index_size,unused)
    exec sp_spaceused @obj

    select @i=@i+1
    end -- while

if @dbg=1
    select
        [name],
        data,
        convert(int,dbo.fn__str_at(data,'''',1)) data_n,
        dbo.fn__str_at(data,'''',2) data_t,
        index_size,
        convert(int,dbo.fn__str_at(index_size,'''',1)) index_size_n,
        dbo.fn__str_at(data,'''',2) index_size  ,
        unused,
        convert(int,dbo.fn__str_at(unused,'''',1)) unused_n,
        dbo.fn__str_at(unused,'''',2) unused
    from #info_db
    order by [name]

update #info_db set
    data_n      =convert(int,dbo.fn__str_at(data,'''',1)),
    data        =dbo.fn__str_at(data,'''',2),
    index_size_n=convert(int,dbo.fn__str_at(index_size,'''',1)),
    index_size  =dbo.fn__str_at(data,'''',2),
    unused_n    =convert(int,dbo.fn__str_at(unused,'''',1)),
    unused      =dbo.fn__str_at(unused,'''',2),
    curr_idnt   =ident_current([name])

update #info_db set
    avg_bXrec   =
        (1.0*data_n*case data when ''kb'' then 1024 when ''mb'' then 1024*1024 else -1 end+
         1.0*index_size_n*case index_size when ''kb'' then 1024 when ''mb'' then 1024*1024 else -1 end-
         1.0*unused_n*case unused when ''kb'' then 1024 when ''mb'' then 1024*1024 else -1 end
        ) / [rows]
where [rows]>0

declare cs cursor local for
    select [name],object_id(name),quotename(name)
    from #info_db
open cs
while 1=1
    begin
    fetch next from cs into @obj,@id,@qobj
    if @@fetch_status!=0 break

    select @sql=''
        update #info_db set
        ncols=( select count(*)
                from ''+quotename(@db)+''..syscolumns with (nolock)
                where id=''+convert(sysname,object_id(@obj))+'')
        where [name]=''''''+@obj+''''''
        ''
    exec(@sql)

    if charindex(''|norm|'',@opt)>0
        begin
        if @dbg>0 exec sp__printf ''-- collecting norm.info for %s'',@obj
        select @sql=null
        -- convert all in sql_variant to allow union
        select @sql = isnull(@sql+''union''+@crlf,'''')
                    + ''select cast(''+quotename(c.name)+'' as sql_Variant) a ''
                    + ''from ''+@qobj+'' with (nolock) ''
                    + ''where not ''+quotename(c.name)+'' is null''
                    + @crlf
        from sys.columns c
        join sys.types t on c.user_type_id=t.user_type_id
        where object_id=@id
        -- select * from sys.types
        and not t.name in ( ''image'',''text'',''ntext'',''xml'',
                            ''varbinary'',''binary'',
                            ''varchar'',''nvarchar'',
                            ''char'',''nchar''
                           )
        and not t.max_length<8
        -- sum lens (+8 meand id and ref id)
        select @sql = ''select ''
                    + ''@n=sum(datalength(a)+8)/1024 ''
                    + ''from (''+@crlf+@sql+'') tbl''+@crlf
        exec @ret=sp_executesql @sql,N''@n int out'',@n=@n out
        if @ret!=0
            begin
            exec sp__printsql @sql
            goto err_cod
            end

        -- add blob & binary data
        select @sql = @sql
                    + ''select ''
                    + ''@n=@n+sum(datalength(''+quotename(c.name)+'')+8) ''
                    + ''from ''+@qobj+@crlf
                    + ''where not ''+quotename(c.name)+'' is null''
                    + @crlf
        from sys.columns c
        join sys.types t on c.user_type_id=t.user_type_id
        where object_id=@id
        and t.name in (''image'',''text'',''ntext'',''xml'',''varbinary'',''binary'')
        exec @ret=sp_executesql @sql,N''@n int out'',@n=@n out
        if @ret!=0
            begin
            exec sp__printsql @sql
            goto err_cod
            end

        -- varchar(max) cannot converted as variant but simply distuinguisced
        select @sql=null
        select @sql = isnull(@sql+''union''+@crlf,'''')
                    + ''select cast(''+quotename(c.name)+'' as nvarchar(max)) a ''
                    + ''from ''+@qobj+'' with (nolock) ''
                    + ''where not ''+quotename(c.name)+'' is null''
                    + @crlf
        from sys.columns c
        join sys.types t on c.user_type_id=t.user_type_id
        where object_id=@id
        and t.name in ( ''varchar'',''nvarchar'',''char'',''nchar'' )
        -- sum lens
        select @sql = ''select ''
                    + ''@n=@n+sum(datalength(a)+8)/1024 ''
                    + ''from (''+@crlf+@sql+'') tbl''+@crlf
        exec @ret=sp_executesql @sql,N''@n int out'',@n=@n out
        if @ret!=0
            begin
            exec sp__printsql @sql
            goto err_cod
            end

        -- max_datalen<8 for data non normalizable
        select @sql=null
        select @sql = isnull(@sql+@crlf,'''')
                    + ''select @n=@n+sum(datalength(''+quotename(c.name)+''))/1024 ''
                    + ''from ''+@qobj+'' with (nolock) ''
                    + ''where not ''+quotename(c.name)+'' is null''
                    + @crlf
        from sys.columns c
        join sys.types t on c.user_type_id=t.user_type_id
        where object_id=@id
        and t.max_length<8
        exec @ret=sp_executesql @sql,N''@n int out'',@n=@n out
        if @ret!=0
            begin
            exec sp__printsql @sql
            goto err_cod
            end

        -- finally update tbl info
        exec(''update #info_db set norm=@n where name=''''''+@obj+'''''''')

        end -- norm

    if charindex(''|density|'',@opt)>0
        begin
        -- sp__info_db #,@dbg=1,@opt=''density''
        if @dbg>0 exec sp__printf ''-- collecting density info for %s'',@obj
        select @sql=null
        -- convert all in sql_variant to allow union
        select @sql = isnull(@sql+''+''+@crlf,'''')
                    + ''(select count(*) ''
                    + ''from ''+@qobj+'' with (nolock) ''
                    + ''where ''+quotename(c.name)+'' is null)''
                    + @crlf
        -- select top 10 *
        from sys.columns c
        join sys.types t on c.user_type_id=t.user_type_id
        where object_id=@id and c.is_computed=0
        select @sql=''select @n=''+@crlf+@sql
        select @n=0
        exec @ret=sp_executesql @sql,N''@n int out'',@n=@n out
        if @ret!=0
            begin
            exec sp__printsql @sql
            goto err_cod
            end
        if @n>0
            begin
            select @sql=''update #info_db set ''+
                 ''norm=100-((1.0*@n/(1.0*rows*ncols))*100.0) ''+
                 ''where name=''''''+@obj+''''''''
            exec @ret=sp_executesql @sql,N''@n int out'',@n=@n out
            if @ret!=0
                begin
                exec sp__printsql @sql
                goto err_cod
                end
            end -- n>0
        end -- density

    if charindex(''|rwstat|'',@opt)>0
        begin
        -- sp__info_db ''#'',@opt=''rwstat''
        select @sql=''
            update nfo set useeks=user_seeks,uscans=user_scans,ulckps=user_lookups
            from #info_db nfo
            join (
                select object_name(ustat.object_id) as name,
                    /*
                    proportion_of_reads=case
                        when sum(user_updates + user_seeks + user_scans + user_lookups) = 0 then null
                        else cast(sum(user_seeks + user_scans + user_lookups) as decimal)
                                    / cast(sum(user_updates
                                                + user_seeks
                                                + user_scans
                                                + user_lookups) as decimal(19,2))
                        end,
                    proportion_of_writes=case
                        when sum(user_updates + user_seeks + user_scans + user_lookups) = 0 then null
                        else cast(sum(user_updates) as decimal)
                                / cast(sum(user_updates
                                            + user_seeks
                                            + user_scans
                                            + user_lookups) as decimal(19,2))
                        end,
                    sum(user_seeks + user_scans + user_lookups) as total_reads
                    sum(user_updates) as total_writes
                    */
                    sum(user_seeks) as user_seeks,
                    sum(user_scans) as user_scans,
                    sum(user_lookups) as user_lookups
                from sys.dm_db_index_usage_stats as ustat
                join sys.indexes as i
                    on ustat.object_id = i.object_id
                        and ustat.index_id = i.index_id
                join sys.tables as t
                    on t.object_id = ustat.object_id
                where i.type_desc in ( ''''clustered'''', ''''heap'''' )
                group by ustat.object_id
                ) as idxs
                on nfo.name=idxs.name
                ''
        exec @ret=sp_executesql @sql,N''@n int out'',@n=@n out
        if @ret!=0
            begin
            exec sp__printsql @sql
            goto err_cod
            end
        end -- rwstat

    end -- while of cursor
close cs
deallocate cs

if @noout=1
    begin
    select *
    from #info_db
    order by (data_n-unused_n+index_size_n) desc
    drop table #info_db
    end

if @nodrop=0 drop table #objs

goto ret

-- =================================================================== errors ==

err_lso:    exec @ret=sp__err ''local server only'',@proc goto ret
err_cod:    exec @ret=sp__err ''inside sql code'',@proc goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    list info about space used by table objects

Parameters
    @objs       db, schema and object name to inspect
                obj can use wildchar like % and _
                multiple group of objects can be separated by | (pipe)
                if @objs is #, means all tables of current db
    @opt        options
                norm    fill norm row with estime of normalized data(very slow)
                        (work only on objects of local db)
                density fill norm row with % of rapport between not null values
                        and n. of rows
                rwstat  fill useeks, uscans, ulckps

    #info_db    fill this table instead show results.
                create table #info_db (
                    id int identity,

                    [name] sysname,
                    [rows] int          null,
                    reserved sysname    null,
                    data sysname        null,
                    index_size sysname  null,
                    unused sysname      null,

                    max_row_len int     null,
                    data_n int          null,
                    index_size_n int    null,
                    unused_n int        null,
                    avg_bXrec real      null,
                    ncols smallint      null,   -- number of columns
                    curr_idnt bigint    null,   -- last/max identity value
                    norm bigint         null,   -- size of normalized data or density

                    useeks bigint       null,   -- whole index user seeks
                    uscans bigint       null,   -- whole index user scans
                    ulckps bigint       null    -- whole index user lookups
                )

Notes
    If table #info_db is passed, data is returned and not shown
    Can be passed #objs to sub select tables
        create table #objs(id int identity,obj sysname)

Examples
    exec sp__info_db ''''te%''''

'',@p1=@tbl_def
select @ret=-1
ret:
return @ret
end -- sp__info_db'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__info_db:

-- ================================================================= sp__info_io
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__info_io',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130707
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__info_io') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__info_io') with nowait
        goto skip_sp__info_io
        end
    if @ver>130707
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__info_io') with nowait
        goto skip_sp__info_io
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__info_io') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__info_io'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__info_io]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:disk,drives,files,virtual,db,performance,statistics,io
    v:130707\s.zaglio: give info about disk io
    c:from david wiseman (http://www.wisesoft.co.uk)
*/
CREATE proc sp__info_io
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
        |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
-- if  @run=0 goto help


-- =============================================================== #tbls init ==

-- ===================================================================== body ==

-- i/o stats by database
select d.name as databasename,
    round(cast(sum(num_of_bytes_read+num_of_bytes_written) as float) / sum(sum(num_of_bytes_read+num_of_bytes_written)) over() *100,2) as [% total i/o],
    round(cast(sum(num_of_bytes_read) as float) / sum(sum(num_of_bytes_read)) over() *100,2) as [% read i/o],
    round(cast(sum(num_of_bytes_written) as float) / sum(sum(num_of_bytes_written)) over() *100,2) as [% write i/o],
    round(cast(sum(num_of_bytes_read+num_of_bytes_written)/(1024*1024*1024.0) as float),2) as [total gb],    
    round(cast(sum(num_of_bytes_read)/(1024*1024*1024.0) as float),2) as [read gb],
    round(cast(sum(num_of_bytes_written)/(1024*1024*1024.0) as float),2) as [write gb],
    sum(io_stall) as [i/o total wait ms],
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall)) over() /86400000,0) as varchar),'''')))
            --    i/o stall in days
            + cast(sum(io_stall) / 86400000 as varchar)
            --  length of max i/o stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall)/1000,0),114),8) as [i/o total wait time {days} hh:mm:ss],
    sum(io_stall_read_ms) as [i/o read wait ms]    ,
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o read stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall_read_ms)) over() /86400000,0) as varchar),'''')))
            --    i/o read stall in days
            + cast(sum(io_stall_read_ms) / 86400000 as varchar)
            --  length of max i/o read stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall_read_ms)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o read stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall_read_ms)/1000,0),114),8) as [i/o read wait time {days} hh:mm:ss]    ,
    sum(io_stall_write_ms) as [i/o write wait ms],
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o write stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall_write_ms)) over() /86400000,0) as varchar),'''')))
            --    i/o write stall in days
            + cast(sum(io_stall_write_ms) / 86400000 as varchar)
            --  length of max i/o write stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall_write_ms)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o write stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall_write_ms)/1000,0),114),8) as [i/o write wait time {days} hh:mm:ss],    
    sum(io_stall) / nullif(sum(num_of_reads+num_of_writes),0)     as [avg i/o wait ms],
    sum(io_stall_read_ms) / nullif(sum(num_of_reads),0)     as [avg read i/o wait ms],
    sum(io_stall_write_ms) / nullif(sum(num_of_writes),0)     as [avg write i/o wait ms],
    sum(num_of_bytes_read+num_of_bytes_written)/nullif(sum(num_of_reads+num_of_writes),0) as [avg i/o bytes],
    sum(num_of_bytes_read)/nullif(sum(num_of_reads),0) as [avg read i/o bytes],
    sum(num_of_bytes_written)/nullif(sum(num_of_writes),0) as [avg write i/o bytes],    
    cast(max(sample_ms) / 86400000 as varchar)
            -- i/o write stall formatted to hh:mm:ss    
             + '' '' + left(convert(varchar,dateadd(s,max(sample_ms)/1000,0),114),8) as [sample time {days} hh:mm:ss]      
from sys.dm_io_virtual_file_stats(null,null) vfs
join sys.databases d on vfs.database_id = d.database_id
group by d.name
order by [% total i/o] desc;

-- i/o stats by file
select d.name as databasename,
    mf.name as logical_name,
    mf.physical_name,
    round(cast(sum(num_of_bytes_read+num_of_bytes_written) as float) / sum(sum(num_of_bytes_read+num_of_bytes_written)) over() *100,2) as [% total i/o],
    round(cast(sum(num_of_bytes_read) as float) / sum(sum(num_of_bytes_read)) over() *100,2) as [% read i/o],
    round(cast(sum(num_of_bytes_written) as float) / sum(sum(num_of_bytes_written)) over() *100,2) as [% write i/o],
    round(cast(sum(num_of_bytes_read+num_of_bytes_written)/(1024*1024*1024.0) as float),2) as [total gb],    
    round(cast(sum(num_of_bytes_read)/(1024*1024*1024.0) as float),2) as [read gb],
    round(cast(sum(num_of_bytes_written)/(1024*1024*1024.0) as float),2) as [write gb],
    sum(io_stall) as [i/o total wait ms],
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall)) over() /86400000,0) as varchar),'''')))
            --    i/o stall in days
            + cast(sum(io_stall) / 86400000 as varchar)
            --  length of max i/o stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall)/1000,0),114),8) as [i/o total wait time {days} hh:mm:ss],
    sum(io_stall_read_ms) as [i/o read wait ms]    ,
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o read stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall_read_ms)) over() /86400000,0) as varchar),'''')))
            --    i/o stall in days
            + cast(sum(io_stall_read_ms) / 86400000 as varchar)
            --  length of max i/o read stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall_read_ms)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o read stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall_read_ms)/1000,0),114),8) as [i/o read wait time {days} hh:mm:ss]    ,
    sum(io_stall_write_ms) as [i/o write wait ms],
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o write stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall_write_ms)) over() /86400000,0) as varchar),'''')))
            --    i/o write stall in days
            + cast(sum(io_stall_write_ms) / 86400000 as varchar)
            --  length of max i/o write stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall_write_ms)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o write stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall_write_ms)/1000,0),114),8) as [i/o write wait time {days} hh:mm:ss],
    sum(io_stall) / nullif(sum(num_of_reads+num_of_writes),0)     as [avg i/o wait ms],
    sum(io_stall_read_ms) / nullif(sum(num_of_reads),0)     as [avg read i/o wait ms],
    sum(io_stall_write_ms) / nullif(sum(num_of_writes),0)     as [avg write i/o wait ms],
    sum(num_of_bytes_read+num_of_bytes_written)/nullif(sum(num_of_reads+num_of_writes),0) as [avg i/o bytes],
    sum(num_of_bytes_read)/nullif(sum(num_of_reads),0) as [avg read i/o bytes],
    sum(num_of_bytes_written)/nullif(sum(num_of_writes),0) as [avg write i/o bytes],
    cast(max(sample_ms) / 86400000 as varchar)
            -- i/o write stall formatted to hh:mm:ss    
             + '' '' + left(convert(varchar,dateadd(s,max(sample_ms)/1000,0),114),8) as [sample time {days} hh:mm:ss]                                 
from sys.dm_io_virtual_file_stats(null,null) vfs
join sys.databases d on vfs.database_id = d.database_id
join sys.master_files mf on vfs.file_id = mf.file_id and mf.database_id = vfs.database_id
group by d.name,mf.name,mf.physical_name
order by [% total i/o] desc;

-- i/o stats by drive
select left(mf.physical_name,3) as drive,
    round(cast(sum(num_of_bytes_read+num_of_bytes_written) as float) / sum(sum(num_of_bytes_read+num_of_bytes_written)) over() *100,2) as [% total i/o],
    round(cast(sum(num_of_bytes_read) as float) / sum(sum(num_of_bytes_read)) over() *100,2) as [% read i/o],
    round(cast(sum(num_of_bytes_written) as float) / sum(sum(num_of_bytes_written)) over() *100,2) as [% write i/o],
    round(cast(sum(num_of_bytes_read+num_of_bytes_written)/(1024*1024*1024.0) as float),2) as [total gb],    
    round(cast(sum(num_of_bytes_read)/(1024*1024*1024.0) as float),2) as [read gb],
    round(cast(sum(num_of_bytes_written)/(1024*1024*1024.0) as float),2) as [write gb],
    sum(io_stall) as [i/o total wait ms],
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall)) over() /86400000,0) as varchar),'''')))
            --    i/o stall in days
            + cast(sum(io_stall) / 86400000 as varchar)
            --  length of max i/o stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall)/1000,0),114),8) as [i/o total wait time {days} hh:mm:ss],
    sum(io_stall_read_ms) as [i/o read wait ms]    ,
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o read stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall_read_ms)) over() /86400000,0) as varchar),'''')))
            --    i/o stall in days
            + cast(sum(io_stall_read_ms) / 86400000 as varchar)
            --  length of max i/o read stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall_read_ms)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o read stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall_read_ms)/1000,0),114),8) as [i/o read wait time {days} hh:mm:ss]    ,
    sum(io_stall_write_ms) as [i/o write wait ms],
    isnull(nullif(right(replicate(''0'',
            --  length of max i/o write stall in days over resultset (for dynamic padding - replicate)
            len(isnull(cast(nullif(max(sum(io_stall_write_ms)) over() /86400000,0) as varchar),'''')))
            --    i/o write stall in days
            + cast(sum(io_stall_write_ms) / 86400000 as varchar)
            --  length of max i/o write stall in days over resultset (for dynamic padding - right)    
            ,len(isnull(cast(nullif(max(sum(io_stall_write_ms)) over() /86400000,0) as varchar),'''')))
             + '' '','' ''),'''')
            -- i/o write stall formatted to hh:mm:ss    
             + left(convert(varchar,dateadd(s,sum(io_stall_write_ms)/1000,0),114),8) as [i/o write wait time {days} hh:mm:ss],
    sum(io_stall) / nullif(sum(num_of_reads+num_of_writes),0)     as [avg i/o wait ms],
    sum(io_stall_read_ms) / nullif(sum(num_of_reads),0)     as [avg read i/o wait ms],
    sum(io_stall_write_ms) / nullif(sum(num_of_writes),0)     as [avg write i/o wait ms],
    sum(num_of_bytes_read+num_of_bytes_written)/nullif(sum(num_of_reads+num_of_writes),0) as [avg i/o bytes],
    sum(num_of_bytes_read)/nullif(sum(num_of_reads),0) as [avg read i/o bytes],
    sum(num_of_bytes_written)/nullif(sum(num_of_writes),0) as [avg write i/o bytes],
    cast(max(sample_ms) / 86400000 as varchar)
            -- i/o write stall formatted to hh:mm:ss    
             + '' '' + left(convert(varchar,dateadd(s,max(sample_ms)/1000,0),114),8) as [sample time {days} hh:mm:ss]                                 
from sys.dm_io_virtual_file_stats(null,null) vfs
join sys.databases d on vfs.database_id = d.database_id
join sys.master_files mf on vfs.file_id = mf.file_id and mf.database_id = vfs.database_id
group by left(mf.physical_name,3)
order by [% total i/o] desc;

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    returns io statistics by: database,file,drive

Parameters
    [param]     [desc]
    @opt        options
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__info_io'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__info_io:

-- ============================================================= sp__info_server
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__info_server',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130517
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__info_server') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__info_server') with nowait
        goto skip_sp__info_server
        end
    if @ver>130517
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__info_server') with nowait
        goto skip_sp__info_server
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__info_server') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__info_server'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__info_server]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:server,instance,port,ip,address
    v:130517\s.zaglio: get info about server
*/
CREATE proc sp__info_server
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    -- @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    -- @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
    --     |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
-- if  @run=0 goto help


-- =============================================================== #tbls init ==

-- ===================================================================== body ==
select * into #tmp from fn__server_info()
exec sp__select_asform ''#tmp'',@opt=''noh|dot''
drop table #tmp

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    [write here a short desc]

Parameters
    [param]     [desc]
    @opt        options
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__info_server'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__info_server:

-- ================================================================ sp__info_svr
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__info_svr',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130107
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__info_svr') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__info_svr') with nowait
        goto skip_sp__info_svr
        end
    if @ver>130107
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__info_svr') with nowait
        goto skip_sp__info_svr
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__info_svr') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__info_svr'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__info_svr]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:virtual,file,log,vlf,enterprise,size,db,dbs,server,status
    v:130107\s.zaglio: get global info about entire server
    todo:integrate sp__util_vlf
    t:sp__info_svr detail
*/
CREATE proc sp__info_svr
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    @sel bit,@print bit,@detail bit,
    @end_declare bit

-- =========================================================== initialization ==
select
    @detail=charindex(''|detail|'',@opt),
    @sel=charindex(''|sel|'',@opt),
    @print=charindex(''|print|'',@opt),
    @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
        |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @end_declare=1

if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
-- if  @run=0 goto help

-- =============================================================== #tbls init ==
create table #dbs(
    srv sysname,
    db sysname,
    lname sysname,
    pname nvarchar(1024),
    size int,
    um nvarchar(4),
    density float null,
    unusedvlf int null,
    usedvlf int null,
    totalvlf int null
    )

-- ===================================================================== body ==
if @detail=1
insert #dbs(srv,db,lname,pname,size,um)
select
    @@servername,
    db_name(database_id) as db,
    name as logical_name,
    physical_name,
    (size*8)/1024 size,
    ''MB'' um
from sys.master_files

insert #dbs(srv,db,lname,pname,size,um)
select @@servername,''*'',''*'',''*'',
    case
    when cast(sum(sizemb)/(1024.0*1024.0) as int)=0
    then cast(sum(sizemb)/1024.0 as int)
    else cast(sum(sizemb)/(1024.0*1024.0) as int)
    end as size,
    case
    when cast(sum(sizemb)/(1024.0*1024.0) as int)=0
    then ''GB''
    else ''TB''
    end as size_um
from (
    select
        db_name(database_id) as databasename,
        name as logical_name,
        physical_name, (size*8)/1024 sizemb
    from sys.master_files
    ) sizes

if @sel=1
    select * from #dbs
else
    begin
    exec sp__select_astext ''select * from #dbs''
    -- separate from help
    exec sp__prints ''8<''
    end

-- ================================================================== dispose ==
dispose:
drop table #dbs

goto help

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    get global info about entire server

Notes
    lname is logical name
    pname is physical name

Parameters
    @opt    options
            detail      show detail about all databases, not only the sum

''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__info_svr'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__info_svr:

-- ================================================================ sp__info_tbl
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__info_tbl',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130426
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__info_tbl') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__info_tbl') with nowait
        goto skip_sp__info_tbl
        end
    if @ver>130426
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__info_tbl') with nowait
        goto skip_sp__info_tbl
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__info_tbl') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__info_tbl'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__info_tbl]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:130426\s.zaglio: short comment
    t:sp__info_tbl rep05_delivery_notes
    t:sp__info_tbl rep06_delivery_notes_details,@dbg=1
    t:sp__info_tbl rep07_files,@dbg=1
*/
CREATE proc sp__info_tbl
    @tbl sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    @i int,@n bigint,                       -- index, counter
    -- @sql nvarchar(max),                  -- dynamic sql
    -- options
    -- @sel bit,@print bit,                 -- select and print option for utils
    @col sysname,
    @len int,
    @typ sysname,
    @sql nvarchar(max),
    @oid int,
    @rows bigint,
    @d datetime,@ms int,
    @rf real,                               -- reference factor
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    -- @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
    --    |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @oid=object_id(@tbl),
    @end_declare=1

declare @cols table(
    id smallint identity primary key,
    tbl sysname,
    col sysname,
    typ sysname,
    ln int,
    n_distinct bigint,
    pctw tinyint,           -- whole effective occupation
    pctt tinyint,           -- text fields effective occupation
    ms int,
    idxs tinyint,           -- num of indexes where present
    ok_col bit default(1)
    )

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if @tbl is null goto help

if @oid is null raiserror(''table not found'',16,1)

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

-- count all records
select @rows=dbo.fn__count(@tbl)

insert @cols(tbl,col,typ,ln,n_distinct)
select @tbl,''*'',''*'',0,@rows

insert @cols(tbl,col,typ,ln,n_distinct)
select @tbl,c.name,t.name,c.length,0
from syscolumns c
join systypes t
on c.xusertype=t.xusertype
where id=@oid
and iscomputed=0
order by colorder

update @cols set ok_col=0 where typ in (''*'',''image'')

-- calculate rf
-- rf is a factor used to predict more correctly what happen if we change the
-- colum value with a int reference
-- (4*n.of.cols)/sum(size(n.of.cols))*100
select @rf=4.0*(select count(*) from @cols where ok_col=1 and ln>=4)
          /(select sum(ln) from @cols where ok_col=1 and ln>=4)

if @dbg>0 exec sp__printf ''--rf:%d'',@rf

declare cs cursor local for
    select col
    from @cols
    where ok_col=1
open cs
while 1=1
    begin
    fetch next from cs into @col
    if @@fetch_status!=0 break

    if @dbg>0 exec sp__printf ''-- scanning col:%s'',@col
    select @sql=null,@d=getdate()
    select @sql=''select @n=count(*) ''
               +''from (select distinct [''+@col+''] from [''+@tbl+'']) a''
    exec sp_executesql @sql,N''@n bigint out'',@n=@n out
    exec sp__elapsed @d out,@ms=@ms out
    update @cols set
        n_distinct=@n,
        pctw=cast((1.0*@n)/(@rows*1.0)*100 as int),
        ms=@ms
    where col=@col

    end -- cursor cs
close cs
deallocate cs

update @cols set
    pctw=(select sum(pctw)/count(*) from @cols where ok_col=1),
    pctt=(
        select sum(pctw)/count(*)
        from @cols
        where ok_col=1
        and (typ like ''%char%'' or typ like ''%text%'')
        ),
    ms=(select avg(ms) from @cols where ok_col=1)
where col=''*''

-- correct with rf
update @cols set pctw=pctw+pctw*@rf,pctt=pctt+pctt*@rf

update c set idxs=i.n
from @cols c
join (
    select columnname col,count(*) n
    from fn__script_idx(@oid)
    group by columnname
    ) i on c.col=i.col

-- t:sp__info_tbl log_ddl,@dbg=1
if not object_id(''tempdb..#sp__info_tbl_results'') is null
    insert #info_tbl(tbl,col,typ,ln,n_distinct,pctw,pctt,ms,idxs)
    select tbl,col,typ,ln,n_distinct,pctw,pctt,ms,idxs from @cols
else
    begin
    select tbl,col,typ,ln,n_distinct,pctw,pctt,ms,idxs
    into #info_tbl
    from @cols
    exec sp__select_astext ''select * from #info_tbl order by typ''
    drop table #info_tbl
    end

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    estime the redundancy of data of a table

Note
    will be excluded computed and image columns
    How read results
    pctw    is the percentage of whole data effectively used
    pctt    is the percentage of only text data but effectively used
    ms      is the time required to distinct data;
            in the row * is the average of all rows and can be used
            to estime the access time per rows where calculate the pctw
            to predict how we can gain after a normalization/aggregation
    NB1:    the redundancy is vertical and do not consider couple; so a
            waste of a single column can be normal if is coupled with an other;
            for example the bill address can redundant alone but correct if
            coupled with the ship address
    NB2:    the pct above 100% are normals because is considered an index
            called referential index that predict the replacement of a value
            with its index; so if pctw/pctt will be above than 100, normally
            mean that the table is already normalized

Parameters
    @tbl        table name
    @opt        options
    #info_tbl   if present fill this table instead of print values
                create table #info_tbl(
                    tbl sysname,
                    col sysname,            -- * is about all columns of table
                    typ sysname,
                    ln int,
                    n_distinct bigint,
                    pctw tinyint,           -- whole effective occupation
                    pctt tinyint,           -- text fields effective occupation
                    ms int,
                    idxs tinyint            -- num of indexes where present
                    )

Examples
    sp__info_tbl log_ddl
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__info_tbl'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__info_tbl:

-- ================================================================== sp__insert
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__insert',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090703
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__insert') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__insert') with nowait
        goto skip_sp__insert
        end
    if @ver>090703
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__insert') with nowait
        goto skip_sp__insert
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__insert') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__insert'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__insert]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:090703\S.Zaglio: added transaction
    r:090626\S.Zaglio: used fn__flds_quotename
    r:090205\S.Zaglio: added header management
    r:090129\S.Zaglio: corrected management of '' in data
    r:081218\S.Zaglio: added random delete and reinsert or a single row and @rows
    r:081021\S.Zaglio: insert constant data or from a select or table
    t:sp__insert ''test'',''v1|v2|v3\v4|v5|v6'',@row_sep=''\'',@dbg=1 select * from test
    t:sp__insert ''ot04_stock'',@excludes=''sync_dt'',@random=1,@dbg=1  -- delete and reinsert a random line
    t:
        ALTER  table test(a int, b sysname, c sysname,d int, e sysname)
        declare @test nvarchar(4000)
        set @test=''19|BATCH|OT03_BATCH_NUMBER|2|(null)
        20|BATCH|OT05_BATCH_DETAILS|3|(null)
        21|BATCH|OT06_BATCH_DOCS|4|(null)
        22|BATCH|A23_BATCH_DOCS|1|(null)
        ''
        exec sp__insert ''test'',@test,@null=''(null)'',@dbg=1
        select * from test
        drop table test
*/
CREATE   proc [dbo].[sp__insert]
    @into sysname=null,
    @from nvarchar(4000)=null,    -- values,table,select
    @flds nvarchar(4000)=null,    -- fields name
    @rows bigint=null out,
    @excludes nvarchar(4000)=null,
    @col_sep nvarchar(8)=''|'',
    @row_sep nvarchar(8)=null,
    @header bit=0,
    @random bit=0,
    @null nvarchar(16)=null,
    @trans bit=1 ,
    @dbg bit=0
as
begin
set nocount on
declare @crlf nvarchar(2) set @crlf=char(13)+char(10)
if @into is null begin
    exec sp__usage ''sp__insert''
    goto ret
end

if @row_sep is null set @row_sep=@crlf

declare @i int, @col int ,@cols int
declare @sql nvarchar(4000)
declare @values nvarchar(4000)
declare @where nvarchar(4000)
declare @table sysname
declare @row nvarchar(4000)
declare @value sysname
declare @consts bit set @consts=0
declare @s sysname
declare @err int

if @random=1 begin
    if @flds is null and @header=0 set @flds=dbo.fn__flds_of(@into,'','',@excludes)
    set @table=@into
    if @where is null set @where=dbo.fn__flds_of(@table,'','',@excludes)
    set @sql=''SELECT top 1 ''+@where+'' FROM ''+@table+'' ORDER BY newid() desc'' -- get a random line
    set @values=''''
    exec sp__select @sql,@body=@values out,@null=''(/null/)'',@dtstyle=126
    set @values=dbo.fn__inject(@values)
    set @where=replace(@where,'','','' and '')
    set @where=dbo.fn__str_exp(dbo.fn__str_exp(''%%=''''@@'''''',@where,'' and ''),@values,''|'')
    set @where=replace(@where,''=''''(/null/)'''''','' is null '')
    set @sql=''DELETE FROM ''+@table+'' WHERE ''+@where
    if @dbg=1 exec sp__printf @sql
    exec(@sql) set @rows=@@rowcount
    if @rows!=1 begin print ''no deleted row'' goto err end
    set @values=dbo.fn__str_exp(''''''%%'''''',@values,''|'')
    set @values=replace(@values,''|'','','')
    set @sql=''insert into %tbl%(%flds%)\n values(%values%)''
    exec sp__str_replace @sql out,''\n|%tbl%|%flds%|%values%|''''(/null/)'''''',@crlf,@table,@flds,@values,''null''
    if @dbg=1 exec sp__printf @sql
    exec(@sql) set @rows=@@rowcount
    if @rows!=1 begin print ''no inserted row'' goto err end
    goto ret
end

if charindex(@col_sep,@from)>0 or charindex(@row_sep,@from)>0 set @consts=1
if @consts=1 begin
    set @rows=dbo.fn__str_count(@from,@row_sep)
    set @cols=dbo.fn__str_count(@from,@col_sep)
end

set @i=1

-- populate flds statement
if @header=1 begin
    set @flds=replace(dbo.fn__str_at(@from,@row_sep,@i),@col_sep,'','')
    set @i=@i+1
end
if @flds is null begin
    set @flds=dbo.fn__flds_of(@into,'','',null)
    end

-- prepare fields names
set @flds=dbo.fn__flds_quotename(@flds,'','')

if @trans=1 begin
    if @dbg=1 exec sp__printf ''begin transaction''
    begin transaction
    end
set @null=''''''''+@null+''''''''
while (@i<=@rows)
    begin
    set @row=dbo.fn__str_at(@from,@row_sep,@i)
    set @row=replace(@row,'''''''','''''''''''')
    set @i=@i+1
    if @row='''' continue
    /*
    if dbo.fn__exists(@into,''U'')=0 begin
        set @sql=''select into @tbl select ''
        set @col=1 set @values=''''
        while (@col<@cols) begin
            set @s=str(@col)
            set @value=dbo.fn__str_at(@row,@col_sep,@col)
            if isdate(@value)=1 set @value=''getdate() as col''+@s else
            if isnumeric(@value)=1 set @value=str(@value)+'' as col''+@s else
            set @value=''''''''+replicate(''-'',255)+'''''' as col''+@s
            if @values<>'''' set @values=@values+'',''
            set @values=@values+@value
        end -- while
    */
    set @values=''''''%%''''''
    set @values=dbo.fn__str_exp(@values,@row,@col_sep)
    set @values=replace(@values,@col_sep,'','')
    set @values=replace(@values,@null,''null'')
    set @sql=''insert into @tbl(@flds) values(@from) set @err=@@error''

    /*
    if @consts=1 begin
        set @values=dbo.fn__str_exp(''''''%%'''''',@from,@col_sep)
        set @values=replace(@values,@col_sep,'','')
        set @from=''values(''+@values+'')''
    end
    */

    exec sp__str_replace @sql out,''@tbl|@flds|@from'',@into,@flds,@values
    if @dbg=1 exec sp__printf @sql
    exec sp_executesql @sql,N''@err int out'',@err=@err out
    if @err!=0 goto err
    end -- while

if @trans=1 begin
    if @dbg=1 exec sp__printf ''commit transaction''
    commit transaction
    end
goto ret

err:
if @trans=1 begin
    if @dbg=1 exec sp__printf ''rollback transaction''
    rollback
    end

ret:
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__insert:

-- ====================================================================== sp__io
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__io',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120515
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__io') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__io') with nowait
        goto skip_sp__io
        end
    if @ver>120515
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__io') with nowait
        goto skip_sp__io
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__io') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__io'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__io]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,io
    v:120515\s.zaglio: total revision
    v:120514\s.zaglio: support I/O between files and application
*/
CREATE proc sp__io
    @cod nvarchar(4000) = null,
    @id  int            = null out,
    @opt sysname        = null,
    @dbg int            = 0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @cod is null goto help

-- ============================================================== declaration ==
declare
    @end_declare bit

-- =========================================================== initialization ==
select
    @end_declare=1

-- ======================================================== second params chk ==

-- ===================================================================== body ==

-- drop table iof drop view iof_list
if object_id(''iof'') is null
    begin
    create table iof(
        tid     tinyint,
        id      int identity(-2147483648,1) constraint pk_iof primary key,
        rid     int not null,           -- parent
        pid     int null,
        flags   smallint not null,
        wrong       as cast(iof.flags & dbo.fn__flags(''F'') as bit),
        processed   as cast(iof.flags & dbo.fn__flags(''G'') as bit),
        idx     int null,               -- line number
        txt     nvarchar(4000) not null,-- file/path/... name or line of text
        dt      datetime null           -- date of file
        )
    create index ix_iof_rid on iof(rid,id)
    end -- io_files

-- tids
select @id=null
select @id=id from tids,iof where iof.tid=tids.grp and txt=@cod
if @@rowcount=0
    begin
    -- truncate table iof
    -- sp__io null,''test''
    begin tran
    insert iof(tid,rid,flags,txt)
    select tids.grp,0,0,@cod from tids
    select @id=@@identity
    update iof set rid=@id where id=@id
    commit
    end

goto ret

-- =================================================================== errors ==
err_nid:
exec @ret=sp__err ''code for tid must be specified'',@proc
goto ret

err_trn:
exec @ret=sp__err ''inside transaction'',@proc
goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    support I/O between files and application
    If not exists, create/alter table "iof" and other structures.
    See flags.iof.* for other specifics.

Parameters
    @id out     id of record
    @cod        name of group
    @opt        options (not used)

Examples
    -- insert a new grp record and/or return the id is already exists
    -- if necessary, creare/update structures
    exec sp__io @id=@gid out,@cod=''''my group''''

    -- store a text file

    -- header
    insert iof(tid,rid,flags,txt)
    select tids.[file],@gid,0,''''filename.ext'''' from tids
    select @fid=@@identity

    -- rows
    insert iof(tid,rid,flags,idx,txt)
    select tids.code,@fid,0,txt.lno,txt.line
    from tids,#src as txt
    order by txt.lno

    -- list files of my group
    select txt from tids,iof where iof.tid=tids.file and iof.rid=@gid
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__io'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__io:

-- ===================================================================== sp__job
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__job',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131126
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__job') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__job') with nowait
        goto skip_sp__job
        end
    if @ver>131126
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__job') with nowait
        goto skip_sp__job
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__job') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__job'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__job]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,utijob
    v:131126\s.zaglio: near run not working
    v:121108\s.zaglio: better err_nosp and restyle
    v:121010\s.zaglio: added collate database_default
    v:121004\s.zaglio: noew correctly check emails separated by ; or , or |
    v:120613\s.zaglio: captured out of sp_update_job; and tests, see comments
    v:111130\s.zaglio: added @ref
    v:111118.1832\s.zaglio: expanded @job to multiple and added proc info and off option
    v:111110\s.zaglio: added output to #jobs
    v:110720.1638\s.zaglio: some imprecision in help, added warning
    v:110706\s.zaglio: added once schedule
    v:110610\s.zaglio: done and tested each case whoen in help
    r:110609\s.zaglio: reviewed @at parse
    r:110607\s.zaglio: reviewed @at parse
    v:110318\s.zaglio: added quiet
    v:110305\s.zaglio: a small bug near ena/dis job
    v:110212\s.zaglio: added macro %db%
    v:110130\s.zaglio: used sp_update_job to enable/disable jobs
    v:110119\s.zaglio: added DIS option to disable
    v:101222\s.zaglio: added multi schedule
    v:101221\s.zaglio: added multi schedule
    v:101204\s.zaglio: added log,dis,ena in @opt
    v:100919.1320\s.zaglio: better name for steps of sql statements and steps flow
    v:100919.1305\s.zaglio: added delete of log files and adapted to new version of sp__job_status
    v:100919.1225\s.zaglio: a bug in job of single step
    v:100919.1210\s.zaglio: added slot management
    v:100919\s.zaglio: a bug near job with same sp of diff. db
    v:100912\s.zaglio: added last run date/time
    v:100724.1100\s.zaglio: a remake
    t:msdb..sp_help_job  -- msdb.dbo.sp_get_composite_job_info
*/
CREATE proc [dbo].[sp__job]
    @job    sysname = null,
    @sp     nvarchar(4000) = null,
    @at     sysname = null,
    @emails nvarchar(1024) = null,
    @smtp   sysname = null,
    @ref    sysname = null,
    @opt    sysname = null,
    @dbg    int =0
as
begin
set nocount on
declare
    @proc sysname,@ret int,
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select @proc=object_name(@@procid),@ret=0
select @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @dbg=1 exec sp__printf ''-- scope: %s'',@proc

-- 120613\s.zaglio: rarely do not disable without errors
if @@trancount>0 goto err_trn

/*
-- drop table #xp_results
create table #xp_results(
    [job id] uniqueidentifier not null,
    [last run date] int not null,
    [last run time] int not null,
    next_run_date int not null,
    next_run_time int not null,
    next_run_schedule_id int not null,
    requested_to_run int not null, -- bool
    request_source int not null,
    request_source_id sysname collate database_default null,
    running int not null, -- bool
    current_step int not null,
    current_retry_attempt int not null,
    job_state int not null )

insert #xp_results
exec msdb..xp_sqlagent_enum_jobs
    @is_sysadmin = 1,
    @job_owner = ''''
*/

select @job=replace(@job,''%db%'',db_name())
select @job=replace(@job,''*'',''#'')
select @job=replace(@job,''#'',''%'')
if charindex(''%'',@job)>0
    select @job=replace(replace(@job,''_'',''[_]''),'''''''','''''''''''')

select @sp =replace(@sp ,''*'',''#'')

if @job is null
or (@job!=''%''                       -- 120613\s.zaglio: a bug on list
    and charindex(''%'',@job)>0
    and @sp is null
    )
    goto help

select
    @emails=ltrim(rtrim(isnull(@emails,''''))),
    @smtp=ltrim(rtrim(isnull(@smtp,'''')))

if @emails!='''' and @smtp=''''
    select @smtp=ltrim(rtrim(convert(sysname,dbo.fn__config(''smtp_server'',null))))

if @emails!='''' and @smtp='''' goto err_logv

if @job=''%'' and @sp=''#'' goto err_cdaj

if @emails!='''' and not dbo.fn__chk_email(@emails,'';,|'') is null goto err_wrem

-- ============================================================== declaration ==

if @dbg=-1 exec sp__printf ''== declaration ==''

declare
    @cmd_log nvarchar(4000),@n int,
    @jid uniqueidentifier,@sid int,@lid int,@schid int,
    @db sysname,@i int,@dt datetime,
    @freq_type int,@freq_interval int, @freq_subday_type int,
    @freq_subday_interval int,@freq_relative_interval int,
    @freq_recurrence_factor int, @start_date int,
    @end_date int, @start_time int,
    @end_time int,@at_msg sysname,
    @time int,@date int,
    @crlf nvarchar(2),@cmd nvarchar(4000),@step sysname,
    @ofa int,@osa int,@id int,
    @sql nvarchar(4000),
    @log nvarchar(512)

declare @cod table(cod char(3),val tinyint,typ tinyint)
    insert @cod
    /*
    -- month
    select ''jan'',1  union
    select ''feb'',2  union
    select ''mar'',3  union
    select ''apr'',4  union
    select ''may'',5  union
    select ''jun'',6  union
    select ''jul'',7  union
    select ''aug'',8  union
    select ''sep'',9  union
    select ''oct'',10 union
    select ''nov'',11 union
    select ''dec'',12 union
    */
    -- week day  -- print ascii(''D'')
    select ''sun'',1  ,68 union
    select ''mon'',2  ,68 union
    select ''tue'',4  ,68 union
    select ''wed'',8  ,68 union
    select ''thu'',16 ,68 union
    select ''fri'',32 ,68 union
    select ''sat'',64 ,68 union
    -- special day -- print ascii(''S'')
    select ''eom'',1  ,83 union  -- end of month
    select ''fom'',2  ,83        -- 1st of month

declare
    -- select * from msdb..sysschedules
    @ranges table(
        id int identity primary key,

        token varchar(64),
        pos int,

        [start_date] int null,
        [end_date] int null,
        [start_time] int null default(0),
        [end_time] int null default (235959),

        interval int null default (0),
        freq_type int null default (0),
        freq_subday_type int null default (0),
        freq_interval int null default (0),
        freq_subday_interval int null default (0),
        freq_relative_interval int null default (0),
        freq_recurrence_factor int null default (0)
        ) -- @ranges

-- ============================================================== init =====

if @dbg=-1 exec sp__printf ''== init ==''

select @crlf=crlf from fn__sym()

select @i=charindex(''|log:'',@opt)
if @i>0 select @log=substring(@opt,@i+5,charindex(''|'',@opt,@i+5)-@i-5)
if @log=''%temp%'' select @log=null

if charindex(''|sql|'',@opt)>0
    select
        @db=db_name(),
        @cmd=@sp,
        @step=dbo.fn__hex(dbo.fn__crc32(@cmd))
else
    begin
    if @sp!=''#''
        begin
        if @dbg=1 exec sp__printf ''checking if exists sp "%s"'',@sp
        if object_id(@sp) is null goto err_nosp
        end
    select
        @db=isnull(parsename(@sp,3),db_name()),
        @cmd=parsename(@sp,1), -- become name of step
        @step=replace(replace(@db+''.''+@cmd,''.'',''_''),'' '',''_'')
    end

-- search fors single job
select @n=count(*)
from msdb..sysjobs j
where j.name like @job

-- there is/are no jobs to delete
if @n=0 and @sp=''#'' goto ret

if @dbg=1 exec sp__printf ''found %d jobs'',@n

if @n=1
    select @job=[name],@jid=job_id
    from msdb..sysjobs j
    where j.name like @job

if charindex(''|ena|'',@opt)>0 and not @jid is null
and @sp is null
    begin
    /*  this dont work...i don''t know why
        update msdb.dbo.sysjobs set enabled = 1 where job_id=@jid */
    exec @ret=msdb.dbo.sp_update_job @job_id=@jid,@enabled=1

    -- 120613\s.zaglio: rarely do not disable without errors
    if 1!=(select enabled from msdb.dbo.sysjobs where job_id=@jid)
    or @ret!=0
        goto err_upj

    goto ret
    end

if charindex(''|dis|'',@opt)>0
and not @jid is null
and @sp is null
    begin
    /*  this dont work because sqlagent keep info in memory
        update msdb.dbo.sysjobs set enabled = 0 where job_id=@jid */
    exec @ret=msdb.dbo.sp_update_job @job_id=@jid,@enabled=0

    -- 120613\s.zaglio: rarely do not disable without errors
    if 0!=(select enabled from msdb.dbo.sysjobs where job_id=@jid)
    or @ret!=0
        goto err_upj

    goto ret
    end

select @sid=step_id
from msdb..sysjobsteps s
where s.job_id=@jid and [step_name]=@step

-- search if already exist a previous log step
select @lid=step_id,@cmd_log=command
from msdb..sysjobsteps s
where s.job_id=@jid and [step_name]=@proc+''_status''

if not @emails is null
    begin
    if ''##'' in (left(@smtp,2),right(@smtp,2))
        begin
        select @smtp=convert(sysname,dbo.fn__config(substring(@smtp,3,len(@smtp)-4),null))
        end
    else
        begin
        if ''#'' in (left(@smtp,1),right(@smtp,1))
            begin
            select @sql=N''select @smtp=convert(sysname,dbo.fn_config(''''''
                       +substring(@smtp,2,len(@smtp)-2)+'''''',null)''
            exec sp_executesql @sql,N''@smtp sysname out'',@smtp=@smtp out
            end
        end

    if @smtp is null goto err_smtp

    select @cmd_log=''sp__job_status @mins=-1,@jobs=''''''+@job+''''''''
               +'',@to=''''''+@emails+''''''''+coalesce('',@smtp=''''''+@smtp+'''''''','''')
               +isnull('',@ref=''''''+@ref+'''''''','''')
               +'',@opt=''''''
    if charindex(''|sp4|'',@opt)>0 select @cmd_log=@cmd_log+''sp4''
    if charindex(''|sp8|'',@opt)>0 select @cmd_log=@cmd_log+''|sp8''
    select @cmd_log=@cmd_log+''''''''
    end
else
    begin
    if @dbg>0 exec sp__printf ''Warning! Without @emails, the status step is omitted.''
    end


-- ================================================================ list jobs ==

if @dbg=-1 exec sp__printf ''== list jobs ==''

if charindex(''%'',@job)>0 and @sp is null
    begin
    select @cmd_log=''
    select date_modified,[name],[enabled],date_created,version_number
    from msdb..sysjobs j with (nolock)
    where j.name like ''''''+@job+''''''
    order by 1 desc,2 desc
    ''
    exec sp__select_astext @cmd_log
    goto ret
    end -- list of jobs

-- ======================================================== list steps of job ==

if @dbg=-1 exec sp__printf ''== list steps ==''

if not @jid is null and @sp is null and @emails is null and @at is null
and charindex(''|run|'',@opt)=0
    begin
    --    sysjobactivity,sysjobhistory,sysjobs,sysjobschedules
    --    sysjobservers,sysjobsteps,sysjobs_view,sysjobstepslogs >=mssql2k8
    select @cmd_log=''
    select
        s.step_id id,
        s.step_name,
            case
            when len(s.command)>40 then left(s.command,40)+''''...''''
            else s.command
            end
        as command,
        s.last_run_date,s.last_run_time,
        case s.last_run_outcome
        when 0 then ''''not completed''''
        when 1 then ''''completed''''
        when 2 then ''''new try''''
        when 3 then ''''aborted''''
        when 5 then ''''unknown''''
        end las_run_outcome,
        s.database_name,s.output_file_name
    from msdb..sysjobsteps s with (nolock)
    where s.job_id=''''{1}''''
    order by 1''
    exec sp__select_astext @cmd_log,@p1=@jid

    goto ret
    end -- list steps of job


-- ================================================== at parameter management ==

if @dbg=-1 exec sp__printf ''== at parameter manag ==''


-- ##########################
-- ##
-- ## parse schedule
-- ##
-- ########################################################
-- tech info at: http://msdn.microsoft.com/it-it/library/ms366342.aspx

if not @at is null
    begin
    if @dbg=2 exec sp__printf ''calculating schedule...''
    declare @tkn sysname,@tk1 sysname,@tk2 sysname,@tk3 sysname
    declare @ats table (pos int,tkn sysname,typ tinyint)
    insert  @ats(pos,tkn,typ)
    select  pos,token,
            case
            when left(token,1)=''+''
            then 0                                  -- n minutes from now
            when patindex(''%[:;]%'',token)=0 and charindex(''-'',token)>0
            then 90                                 -- start date
            when patindex(''%[;-]%'',token)=0 and charindex('':'',token)>0
            then 80                                 -- time
            when patindex(''%[:;-]%'',token)=0 and
                 (right(token,1) in (''w'',''d'',''h'',''n'') or
                  token in (select cod from @cod))
            then 1                                  -- occurrencies
            when patindex(''%[:;-]%'',token)>0
            then 5                                  -- ranges
            when token=''once''                       -- one time
            then 99
            end
    from dbo.fn__str_table(@at,'''')

    -- adjust times & dates
    update @ats set
        tkn=convert(int,dbo.fn__str_at(tkn,'':'',1)*10000)
           +convert(int,dbo.fn__str_at(tkn,'':'',2)*100)
           +convert(int,isnull(dbo.fn__str_at(tkn,'':'',3),0))
    where typ=80
    update @ats set
        tkn=convert(int,dbo.fn__str_at(tkn,''-'',1)*10000)
           +convert(int,dbo.fn__str_at(tkn,''-'',2)*100)
           +convert(int,dbo.fn__str_at(tkn,''-'',3))
    where typ=90

    -- test how many they are
    if (select count(*) from @ats where typ=80)>2
    or (select count(*) from @ats where typ=90)>2
        goto err_tmdt

    -- set 80,90 to 1st and 81,91 to second
    if (select count(*) from @ats where typ=80)=2
        update @ats set typ=81
        where typ=80
        and tkn= (select max(cast(tkn as int))
                    from @ats
                    where typ=80)

    if (select count(*) from @ats where typ=90)=2
        update @ats set typ=91
        where typ=90
        and tkn= (select max(cast(tkn as int))
                    from @ats
                    where typ=90)

    if @dbg=2 select * from @ats

    select @i=min(pos),@n=max(pos) from @ats

    select
        @freq_type=8,        -- the base is week
        @freq_interval=null, -- if null, all days
        @freq_subday_type=null,
        @freq_subday_interval=0,@freq_relative_interval=0,
        @freq_recurrence_factor=null, @at_msg='''',
        @start_date=convert(int,convert(sysname,getdate(),112)),
        @end_date=99991231

    if exists(select top 1 null from @ats where typ=1)
        -- one range for all occurrencies
        insert @ranges(token,[start_date],start_time,end_time,end_date)
        select ''*'',@start_date,0,235959,@end_date

    -- scan schedule commands
    declare cs cursor local for
        select tkn,typ
        from @ats
        order by typ
    open cs
    while 1=1
        begin
        fetch next from cs into @tkn,@i
        if @@fetch_status!=0 break

        if @i=0     -- NN minutes from now
            insert  @ranges(token,start_time,freq_type,freq_interval,freq_subday_type)
            select  @at,
                    convert  (
                      int,
                      replace(
                        convert(
                          sysname,
                          dateadd(
                            s,convert(int,substring(@at,2,10)),
                            getdate()),
                          8),
                        '':'','''')
                    ),
                freq_time=1, freq_interval=0, freq_subday_type=1

        -- time range (hh:mm:ss-hh:mm:ss;step)
        if @i=5
            begin
            select
                @tk3=dbo.fn__str_at(@tkn,'';'',2),    -- step
                @tk2=dbo.fn__str_at(@tkn,'';'',1),    -- end
                @tk1=dbo.fn__str_at(@tk2,''-'',1),    -- start
                @tk2=dbo.fn__str_at(@tk2,''-'',2)
            -- exec sp__printf ''tk1:%s, tk2:%s, tk3:%s'',@tk1,@tk2,@tk3
            if @tk3='''' goto err_range

            if left(@tk3,1) like ''[1-9]'' and right(@tk3,1) in (''h'',''n'')
                insert @ranges( token,interval,start_time,end_time,freq_type,
                                freq_subday_type,freq_subday_interval,
                                freq_interval)
                select
                    @tkn,
                    interval    =convert(int,left(@tk3,len(@tk3)-1)),
                    start_time  =convert(int,dbo.fn__str_at(@tk1,'':'',1)*10000)
                                +convert(int,dbo.fn__str_at(@tk1,'':'',2)*100)
                                +convert(int,isnull(dbo.fn__str_at(@tk1,'':'',3),0)),
                    end_time    =convert(int,dbo.fn__str_at(@tk2,'':'',1)*10000)
                                +convert(int,dbo.fn__str_at(@tk2,'':'',2)*100)
                                +convert(int,isnull(dbo.fn__str_at(@tk2,'':'',3),0)),
                    freq_type   =case right(@tk3,1)
                                 when ''h'' then 4
                                 when ''n'' then 4
                                 end,
                    freq_subday_type=case right(@tk3,1)
                                     when ''h'' then 8
                                     when ''n'' then 4
                                     end,
                    freq_subday_interval=left(@tk3,len(@tk3)-1),
                    freq_interval=1
            else
                goto err_range
            end -- time range

        if @i=1 -- occurrencies
            begin
            -- exec sp__job ''spj_test'',@at=''48w mon 10:30'',@dbg=2
            -- exec sp__job ''spj_test'',@at=''1w Tue Wed 10:30'',@dbg=2
            -- exec sp__job ''spj_test'',@at=''1w Tue Wed'',@dbg=2

            -- select * from msdb..sysschedules
            select @tk1=right(@tkn,1),@tk2=substring(@tkn,1,len(@tkn)-1)

            if @tkn in (select cod from @cod where typ=68)
                select @freq_interval=@freq_interval|val
                from @cod
                where cod=@tkn and typ=68

            if @tkn=''fom''
                select  @freq_type=32,@freq_interval=8,@freq_relative_interval=1,
                        @freq_subday_type=1

            if @tkn=''eom''
                select  @freq_type=32,@freq_interval=8,@freq_relative_interval=16,
                        @freq_subday_type=1

            if @tk1=''w'' and isnumeric(@tk2)=1
                select @freq_recurrence_factor=@tk2,@freq_subday_type=1 -- specific hour

            if @tk1=''h'' and isnumeric(@tk2)=1
                select @freq_subday_type=8,@freq_subday_interval=@tk2

            if @tk1=''n'' and isnumeric(@tk2)=1
                select @freq_subday_type=4,@freq_subday_interval=@tk2

            update @ranges set
                freq_type=@freq_type,
                freq_interval=isnull(@freq_interval,1|2|4|8|16|32|64),
                freq_subday_type=@freq_subday_type,
                freq_subday_interval=@freq_subday_interval,
                freq_relative_interval=@freq_relative_interval,
                freq_recurrence_factor=@freq_recurrence_factor
            where token=''*''
            end -- occurrencies

        -- set stard/end date/time
        if @i in (90,91,80,81)         -- if not exist at least one schedule...
        and not exists(select null from @ranges)
            insert @ranges(token,[start_date],start_time,end_time,end_date,
                           freq_type,freq_interval)
            select ''*'',@start_date,0,235959,@end_date,
                   4,1

        if @i=90 update @ranges set [start_date]=@tkn
        if @i=91 update @ranges set [end_date]=@tkn
        if @i=80 update @ranges set [start_time]=@tkn
        if @i=81 update @ranges set [end_time]=@tkn

        if @i=99 update @ranges set freq_type=1

        end -- while of cursor for ats
    close cs
    deallocate cs

    -- some last common adjustements
    update @ranges set
        freq_recurrence_factor=isnull(freq_recurrence_factor,1),
        freq_subday_Type=isnull(freq_subday_Type,1)

    if @dbg=2 select * from @ranges

    -- verify
    declare @schedule_description nvarchar(255)
    declare cs cursor local for
        select
            [start_date],end_date,start_time,end_time,
            freq_type,freq_subday_type,freq_interval,
            freq_subday_interval,freq_relative_interval,
            freq_recurrence_factor
        from @ranges
    open cs
    while 1=1
        begin
        fetch next from cs into
            @start_date,@end_date,@start_time,@end_time,
            @freq_type,@freq_subday_type,@freq_interval,
            @freq_subday_interval,@freq_relative_interval,
            @freq_recurrence_factor
        if @@fetch_status!=0 break

        select @schedule_description =''''
        exec msdb..sp_get_schedule_description
          @freq_type=@freq_type,
          @freq_interval=@freq_interval,
          @freq_subday_type=@freq_subday_type,
          @freq_subday_interval=@freq_subday_interval,
          @freq_relative_interval=@freq_relative_interval,
          @freq_recurrence_factor=@freq_recurrence_factor,
          @active_start_date=@start_date,
          @active_end_date=@end_date,
          @active_start_time=@start_time,
          @active_end_time=@end_time,
          @schedule_description=@schedule_description out

        if @schedule_description is null or @dbg=2
            begin
            exec sp__printf ''
            description             = %s
            freq_type               = %d
            freq_interval           = %d
            freq_subday_type        = %d
            freq_subday_interval    = %d
            active_start_date       = %d
            active_start_time       = %d
            active_end_date         = %d
            active_end_time         = %d
            freq_recurrence_fac     = %d''
            ,@schedule_description,
             @freq_type, @freq_interval, @freq_subday_type, @freq_subday_interval,
             @start_date, @start_time, @end_date, @end_time,
             @freq_recurrence_factor
            exec sp__printf ''            freq_relative_interval  = %d'',@freq_relative_interval
            if @schedule_description is null
                begin
                exec sp__printf ''!! warning: null schedule description !!''
                select @at_msg=isnull(@at_msg,''generic'')
                end
            end -- dbg
        end -- while of cursor
    close cs
    deallocate cs

    if @at_msg!='''' goto err_csch

    end -- @at

-- ====================================================== begin job managment ==

if @dbg=-1 exec sp__printf ''== begin job man ==''

begin tran

if @sp=''#''
    begin
    -- delete msg must be visible from user
    exec sp__printf ''deleting job/s(%s)...'',@job
    -- delete job
    while (1=1)
        begin
        select @jid=null
        select top 1 @jid=job_id
        from msdb..sysjobs j
        where j.name like @job
        if @jid is null break
        exec sp__printf ''    deleting job/s(%s)...'',@jid

        -- step into steps and delete log files if exists

        declare cs cursor local for
            select output_file_name
            from msdb..sysjobsteps s
            where job_id=@jid
            and not output_file_name is null
        open cs
        while 1=1
            begin
            fetch next from cs into @log
            if @@fetch_status!=0 break
            exec sp__printf ''      deleting log (%s)...'',@log
            select @cmd=''del /q "''+@log+''"''
            exec master..xp_cmdshell @cmd,no_output
            end -- while of cursor
        close cs
        deallocate cs

        exec @ret=msdb..sp_delete_job @job_id=@jid
        if @@error!=0 or @ret!=0 goto err_jdel
        end -- while

    if @sp=''#'' goto ret
    select @jid=null,@sid=null,@lid=null
    end

-- no error if delete a job that not exists (but no msg is outed)
if @jid is null and @sp=''#'' goto ret

if @jid is null and @sp is null
and @at is null and @emails is null
    goto err_njob

-- if @sp is null and @sid is null and @sp is null
-- and not @at is null  -- change at

-- ============================================================= change email ==

if @dbg=-1 exec sp__printf ''== chg email ==''

if charindex(''|run|'',@opt)=0 and @sp is null and @sid is null
and not @emails is null
    begin
    if @jid is null goto err_njob
    if @dbg=1 exec sp__printf ''updating emails...''
    exec msdb..sp_update_jobstep
        @job_id=@jid,@step_id=@lid,
        @command=@cmd_log
    end -- chg email

-- ====================================================== create update steps ==

if @dbg=-1 exec sp__printf ''== create upd steps ==''

if not @sp is null
    begin

    -- create job if not exists or drop if exists same step name

    if @jid is null
        begin
        if @dbg=1 exec sp__printf ''add new job "%s" on server "(local)"'',@job
        exec msdb..sp_add_job @job_name = @job, @job_id=@jid out
        exec msdb..sp_add_jobserver @job_id=@jid,  @server_name=''(local)''
        end

    if @jid is null goto err_njob

    -- ============================================================ log file name ==

    if @dbg=-1 exec sp__printf ''== log file name ==''

    if charindex(''|nolog|'',@opt)=0
        begin
        if @log is null exec sp__get_temp_dir @log out
        if right(@log,1)!=''\'' select @log=@log+''\''
        select @log=@log+@step+''_log.txt''
        end

    if not @lid is null
        begin
        if @dbg=1 exec sp__printf ''deleting log step (%d)...'',@lid
        exec @ret=msdb..sp_delete_jobstep @job_id=@jid,@step_id=@lid
        if @@error!=0 or @ret!=0 goto ret
        end

    -- ================================================================= add step ==

    if @dbg=-1 exec sp__printf ''== add step ==''

    if charindex(''|elapsed|'',@opt)>0
        select @cmd =''declare @d datetime,@ms int''+@crlf
                    +''exec sp__elapsed @d out''+@crlf
                    +''exec ''+quotename(@sp)+@crlf
                    +''exec sp__elapsed @d,@ms=@ms out''+@crlf
                    +''exec sp__log ''''''+@sp+'''''',@n=@ms''

    if @sid is null
        begin
        if @dbg=1 exec sp__printf ''add step "%s" on db "%s"...'',@step,@db
        exec @ret=msdb..sp_add_jobstep
            @job_id = @jid,
            @step_name = @step,
            -- @step_id = @lid,        -- insert before last
            @subsystem = ''tsql'',
            @command = @cmd,
            @database_name=@db,
            @on_success_action=1,   -- for single job of single step
            @on_fail_action=2,
            @output_file_name=@log,
            @flags=0                -- overwrite log
           --@retry_attempts = 5,
           --@retry_interval = 5
        end
    else
        begin
        if @dbg=1 exec sp__printf ''update step "%s"...'',@step
        exec @ret=msdb..sp_update_jobstep
            @job_id = @jid,
            @step_id = @sid,        -- replace
            @subsystem = ''tsql'',
            @command = @cmd,
            @database_name=@db,
            @on_success_action=3,   -- next step
            @on_fail_action=3,
            @output_file_name=@log,
            @flags=0                -- overwrite log
           --@retry_attempts = 5,
           --@retry_interval = 5
        end

    if @@error!=0 or @ret!=0 goto err_step

    -- ========================================================= re-add log error ==

    if @dbg=-1 exec sp__printf ''== re-add log ==''

    select @lid=null
    if not @cmd_log is null
        begin
        exec @ret=msdb..sp_add_jobstep @job_id = @jid,
            @step_name = ''sp__job_status'',
            @subsystem = ''tsql'',
            @command = @cmd_log,
            @database_name=@db
        if @@error!=0 or @ret!=0 goto err_logs

        -- adjust non log steps
        select @lid=null
        select @lid=step_id
        from msdb..sysjobsteps s
        where s.job_id=@jid and [step_name]=''sp__job_status''

        if @dbg=1 exec sp__printf ''-- added log step with step id=%d'',@lid

        end  -- log

    /*  examples
    step    if_ok     if_err  if_err_ras    if_nolog
    1       next      log     next          end
    2       next      log     next          end
    3       next/end  log     next          end
    log     end       end     end            -
    */

    if @dbg=1 exec sp__printf ''-- align gotos of steps''
    select @i=1,@id=max(step_id) from msdb..sysjobsteps s where s.job_id=@jid

    select @osa=3   -- next
    if charindex(''|ras|'',@opt)>0 select @ofa=3  -- next
    else select @ofa=case when @lid is null then 2 else 4 end -- goto

    while (@i<=@id)
        begin
        if @i=@id select @osa=1,@ofa=2
        if @dbg=1 exec sp__printf ''-- step_id @i=%d, @osa=%d, @ofa=%d, @lid=%d (1=end ok;2=end ko;3=next;4=goto)'',@i,@osa,@ofa,@lid
        exec @ret=msdb..sp_update_jobstep
            @job_id=@jid,@step_id=@i,
            @on_success_action  =@osa,
            @on_fail_action     =@ofa,
            @on_fail_step_id    =@lid
        select @i=@i+1
        end -- while
    -- if @dbg=1 select * from msdb..sysjobsteps where job_id=@jid

    end -- add/chg steps

-- ================================================== run/add/change schedule ==

if @dbg=-1 exec sp__printf ''== add/change schedule ==''

if charindex(''|dis|'',@opt)>0
    begin
    exec sp__printf ''disabling job %s'',@jid
    exec @ret=msdb.dbo.sp_update_job @job_id=@jid,@enabled=0
    -- 120613\s.zaglio: rarely do not disable without errors
    if 0!=(select enabled from msdb.dbo.sysjobs where job_id=@jid)
    or @ret!=0
        goto err_upj
    end


-- ##########################
-- ##
-- ## set schedule
-- ##
-- ########################################################

if not @at is null
    begin
    if @jid is null goto err_njob
    declare cs cursor local for
        select [name]
        -- select *
        from msdb..sysjobschedules j1       -- prob not compatible with mssql2k
        join msdb..sysschedules j2
        on j1.schedule_id=j2.schedule_id
        where job_id=@jid
    open cs
    while 1=1
        begin
        fetch next from cs into @tkn
        if @@fetch_status!=0 break
        if @dbg=1 exec sp__printf ''deleting schedule (%s) for update'',@tkn
        exec @ret=msdb..sp_delete_jobschedule @job_id=@jid,@name=@tkn
        end -- delete schedules
    close cs
    deallocate cs

    -- readd schedules
    declare cs cursor local for
        select
            id,start_date,end_date,start_time,end_time,
            freq_type,freq_subday_type,freq_interval,
            freq_subday_interval,freq_relative_interval,
            freq_recurrence_factor
        from @ranges
    open cs
    while 1=1
        begin
        fetch next from cs into
            @schid,@start_date,@end_date,@start_time,@end_time,
            @freq_type,@freq_subday_type,@freq_interval,
            @freq_subday_interval,@freq_relative_interval,
            @freq_recurrence_factor
        if @@fetch_status!=0 break

        select @tkn=@job+''(''+convert(sysname,@schid)+'')''
        if @dbg=1 exec sp__printf ''adding schedule "%s"'',@tkn
        exec @ret=msdb..sp_add_jobschedule
            @name = @tkn,
            @job_id = @jid,
            @enabled = 1,
            @freq_type = @freq_type,
            @freq_interval = @freq_interval,
            @freq_subday_type = @freq_subday_type,
            @freq_subday_interval = @freq_subday_interval,
            @freq_relative_interval = @freq_relative_interval,
            @freq_recurrence_factor = @freq_recurrence_factor,
            @active_start_time = @start_time,
            @active_start_date = @start_date,
            @active_end_time = @end_time,
            @active_end_date = @end_date
        end -- while add schedule
    close cs
    deallocate cs

    end -- schedule

goto ret

-- =================================================================== errors ==
err:exec @ret=sp__err @e_msg,@proc,@p1=@e_p1                            goto ret
err_njob:select @e_msg=''no job found or created''                        goto err
err_aani:select @e_msg=''at changes not implemented again''               goto err
err_eani:select @e_msg=''email changes not implemented again''            goto err
err_nsql:select @e_msg=''SQL option not implemented again''               goto err
err_nosp:select @e_msg=''stored proc. %s not found'',@e_p1=@sp            goto err
err_step:select @e_msg=''creating step''                                  goto err
err_logs:select @e_msg=''creating log step''                              goto err
err_wrem:select @e_msg=''wrong email (%s)'',@e_p1=@emails                 goto err
err_csch:select @e_msg=''calculating schedule:%s'',@e_p1=@at_msg          goto err
err_jdel:select @e_msg=''deleting jobs''                                  goto err
err_cdaj:select @e_msg=''cannot delete all jobs''                         goto err
err_smtp:select @e_msg=''smtp server not specified''                      goto err
err_logv:select @e_msg=''smtp or emails empty''                           goto err
err_range:select @e_msg=''missing or wrong step in range''                goto err
err_tmdt:select @e_msg=''too mach times or dates''                        goto err
err_trn:select @e_msg=''this sp cannot be executed into a transaction''   goto err

err_upj:
/*  not necessary because hope that sp_update_job shown its errors
    exec @ret=sp__err ''sp_update_job failed'',@proc
*/
goto ret

-- ===================================================================== help ==

help:
--     month                   jan,feb,mar,apr,may,jun,jul,aug,sep,oct,nov,dec (only one)
exec sp__usage @proc,''
Scope
    create and maintain jobs with log and error management

Parameters
    #jobs   optional table where store running jobs
    @job    is the name of job to manage;
            accept # as wild char and multiple roots
            separated by pipe ("|")
    @sp     is the name of stored that the step call
            (if already exists, will be replaced with new info)
            if is "#", the job will be deleted
    @at     time of execution (see below)
    @email  emails wich send the log in case of failure of job
            Use "ras" to run all steps before send the lists
            of failed
    @ref    is a generic text passed directly to sp__job_status
    @opt    options
    @dbg    2 show a more detailed schedule description

Options
    ras      continue to next step if current fail
    log:path alternative path for output file (by default a %temp%\@db@proc_log.txt file is created)
    nolog    do not output to file
    elapsed  enable tracing of executions with sp__log
    sql      consider @sp as a generic sql
    run      start the job immediatelly
    quiet    do not show msg about run of job (succesfull...errors..etc)
    sp4      send one email every 4h even if the jub fail every 5 minutes
    sp8      send one email every 8h
    dis      disable the job (also immediatelly, see exampe)
    ena      enabled the job (also immediatelly, see exampe)
    off      print scripts to disable and re-enable active jobs

Time expressions
    yyyy-mm-dd              start date
    hh:nn:ss                every day at hh:nn if start date unspecified
    ??w                     every ?? weeks
    ??h                     every ?? hours
    ??n                     every ?? minutes
    day                     Mon Tue Wed Thu Fri Sat Sun (multiple option)
    +NNNNN                  start after NN seconds
    hh:mm-hh:mm;step        interval; step can be every ?h, ?m, ?d, ?n (see above)
    fom                     1st of month
    eom                     end of month
    once                    execute only one time

Optional output table for running jobs (returned by master..xp_sqlagent_enum_jobs)
    create table #jobs(
        id int   identity,
        [server] sysname,
        [name]   sysname,
        [description]   nvarchar(512),
        job_id          uniqueidentifier,
        last_run_date   int,
        last_run_time   int,
        -- process info
        spid    int null,
        [from]  nvarchar(32) null,
        [sql]   nvarchar(256) null
        )

Examples:

    -- help and list of running jobs
    sp__job

    -- list of jobs
    sp__job #       -- or ''''*''''

    -- list of job by root
    sp__job myj#

    -- add 1st step (and create the job) and the second
    sp__job ''''my job'''',''''sp_test''''
    sp__job ''''my job'''',''''sp_test1''''

    -- list steps
    sp__job ''''my job''''

    -- delete job
    sp__job ''''my_job'''',#

    -- add sql step
    sp__job ''''my_job'''',''''sp__log #my_job'''',@opt=''''sql''''

    -- add/chg error control and email to wich send log
    sp__job ''''my_job'''',@email=''''fname.lname@mails.org''''

    -- add/chg schedule
    sp__job ''''my_job'''',@at=''''10:30''''            -- every day at 10:30
    sp__job ''''my_job'''',@at=''''10:30-20:30;5h''''   -- start at 10:30 end at 20:30 every 5 hours
                                                -- multi time
    sp__job ''''my_job'''',@at=''''00:00-08:00;2h 08:00-21:00;20n 21:00-23:59;2h''''
    sp__job ''''my_job'''',@at=''''Tue''''              -- every Tuesday at midnight
    sp__job ''''my_job'''',@at=''''Sat Sun 10:30''''    -- every weekend at 10:30
    sp__job ''''my_job'''',@at=''''Sat Sun 8:00 21:00 20n''''    -- vertical range
    sp__job ''''my_job'''',@at=''''5n''''               -- every five minutes from midnight

    sp__job ''''my_job'''',@at=''''48w mon 10:30''''    -- every 1st monday in december
                                              -- at 10:30

    sp__job ''''my_job'''',@at=''''fom 10:30''''         -- every 1st of month at 10:30
    sp__job ''''my_job'''',@at=''''eom 10:30''''         -- every end of month at 10:30

    sp__job ''''my_job'''',@at=''''+5''''            -- start 5 minutes from now

    sp__job ''''my_job'''',@at=''''2011-07-14 20:20'''' -- start the 14 jul, every day at 20:20
    sp__job ''''my_job'''',@at=''''2011-07-14 20:20 once'''' -- start the 14 jul, only one time at 20:20

    -- delete a job
    sp__job ''''my_job'''',#

    -- delete my jobs
    sp__job ''''my#'''',#

    -- disable a job
    sp__job ''''my job'''',@opt=''''dis''''

    -- print script to turn off and on active jobs (excluding disabled)
    sp__job ''''myjobs1#|myjobs2#'''',@opt=''''off''''

''
-- list running
-- declare @cmd_log nvarchar(4000),@name sysname,@n int,@ret int

if object_id(''tempdb..#jobs'') is null
    create table #jobs(
        id int identity,
        [server] sysname,
        [name] sysname,
        [description] nvarchar(512),
        job_id uniqueidentifier,
        last_run_date int,
        last_run_time int,
        -- process info
        spid int null,
        [from] nvarchar(32) null,
        [sql] nvarchar(256) null
        )

declare @filter table(job sysname)
insert @filter select token from fn__str_table(@job,''|'')

select @sql=replace(''
    select * into #xp_results -- select *
    from openrowset(
        "sqloledb",
        "server=''+@@servername+'';trusted_connection=yes",
        "set fmtonly off
         exec msdb..xp_sqlagent_enum_jobs @is_sysadmin = 1, @job_owner = """"
        ")'',
    ''"'',
    '''''''')

select @sql=@sql+''
    insert #jobs(
        [server],
        [name],
        [description],
        job_id,
        last_run_date,
        last_run_time
        )
''
-- select top 1 * from msdb..sysjobs
if dbo.fn__isMSSQL2K()=1
    select @sql=@sql+''
    select top 100 percent
        j.originating_server,
        j.name,
        j.description,
        j.job_id,
        r.[last run date] as start_date,
        r.[last run time] as start_time
    from #xp_results r join msdb..sysjobs j with (nolock) on r.[job id]=j.job_id
    where r.running = 1
    order by 1,2''
else
    select @sql=@sql+''
    select top 100 percent
        s.srvname originating_server,
        j.name running_job_name,
        j.description,
        j.job_id,
        r.[last run date] as start_date,
        r.[last run time] as start_time
    from #xp_results r join msdb..sysjobs j on r.[job id]=j.job_id
    join master..sysservers s with (nolock) on s.srvid=j.originating_server_id
    where r.running = 1
    order by 1,2''

if @dbg=1 exec sp__printsql @sql
exec(@sql)
if @@error!=0 exec sp__printsql @sql

if not @job is null
    delete from #jobs
    where not id in (
        select id
        from #jobs j
        join @filter f
        on j.[name] like f.job
        )

update #jobs set
    spid=procs.spid,
    [from]=procs.batch_duration,
    [sql]=left(procs.sql,256)
from #jobs
join (
    select
        p.spid
    ,   right(convert(varchar,
                dateadd(ms, datediff(ms, p.last_batch, getdate()), ''1900-01-01''),
                121), 12) as ''batch_duration''
    ,   case
                when p.program_name like ''sqlagent - tsql jobstep (job %''
                then (
                    select top 1 j.name
                    from msdb..sysjobs j
                    where
                        rtrim(ltrim(dbo.fn__str_between(p.program_name,''job '','' :'',default)))
                        =
                        dbo.fn__hex(convert(varbinary,job_id))
                    )
                else object_name((select top 1 objectid from ::fn_get_sql(p.sql_handle)))
            end
        as obj
    ,   case
                when p.program_name like ''sqlagent - tsql jobstep (job %''
                then (
                    select top 1 j.job_id
                    from msdb..sysjobs j
                    where
                        rtrim(ltrim(dbo.fn__str_between(p.program_name,''job '','' :'',default)))
                        =
                        dbo.fn__hex(convert(varbinary,job_id))
                    )
                else null
            end
        as job_id
    ,   p.hostname
    ,   p.loginame
    ,   substring((select top 1 text from ::fn_get_sql(p.sql_handle)),
                coalesce(nullif(case p.stmt_start when 0 then 0 else p.stmt_start / 2 end, 0), 1),
                case (case p.stmt_end when -1 then -1 else p.stmt_end / 2 end)
                    when -1
                            then datalength((select top 1 text from ::fn_get_sql(p.sql_handle)))
                    else
                            ((case p.stmt_end when -1 then -1 else p.stmt_end / 2 end) -
                             (case p.stmt_start when 0 then 0 else p.stmt_start / 2 end)
                            )
                    end
        ) as [sql]
    from master.dbo.sysprocesses p
    where p.spid!=@@spid
    and p.spid > 50
    and      p.status not in (''background'', ''sleeping'')
    and      p.cmd not in (''awaiting command''
                        ,''mirror handler''
                        ,''lazy writer''
                        ,''checkpoint sleep''
                        ,''ra manager'')
    and p.sql_handle!=0x0
    -- order by batch_duration desc
    ) procs
on procs.job_id=#jobs.job_id

select * from #jobs order by id

if charindex(''|off|'',@opt)>0
    select
        ''update msdb..sysjobs set [enabled]=''+switch+'' where job_id=''''''+
        convert(varchar(64),j.job_id)+'''''' -- ''+j.[name]+'' (''+[description]+'')''
    from (select ''0'' switch union select ''1'' switch) switches,
        (
        select j.*
        from msdb..sysjobs j
        join @filter f
        on j.[name] collate database_default like f.job collate database_default
        where [enabled]=1
        ) j
    order by switch,j.name

set @ret=-1

goto ret

-- ===================================================================== exit ==
ret:
if @@trancount>0
    if @ret=0 commit else rollback

if @ret=0 and charindex(''|run|'',@opt)>0
    begin
    if charindex(''|quiet|'',@opt)=0
        exec msdb..sp_start_job @job_id = @jid
    else
        -- unfortunatelly is not possible manage error 22022
        -- even if the running job are checked
        exec msdb..sp_start_job
            @job_id = @jid,
            @output_flag =0
    end -- run

return @ret
end -- sp__job #'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__job:

-- ============================================================= sp__job_monitor
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__job_monitor',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=101116
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__job_monitor') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__job_monitor') with nowait
        goto skip_sp__job_monitor
        end
    if @ver>101116
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__job_monitor') with nowait
        goto skip_sp__job_monitor
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__job_monitor') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__job_monitor'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__job_monitor]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,utijob
    v:101116\s.zaglio: a bug near fn_config/fn__config sql
    v:100929.1000\s.zaglio: install a job monitor for other monitor
    t:exec sp__job_monitor ''stezagl@tin.it'',''lupin''
*/
CREATE proc sp__job_monitor
    @to         nvarchar(1024) = null,
    @smtp       nvarchar(1024) = null,
    @jobs       nvarchar(1024) = null,
    @excludes   nvarchar(1024) = null,
    @h          int            = null,
    @dbg int=0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname,  @ret int -- standard api: 0=ok -1=help, any=error id
select @proc=''sp__job_monitor'', @ret=0

if @to is null or @smtp is null goto help

-- declarations
declare
    @at sysname,@sp nvarchar(4000),@mins int,
    @jname sysname,@sql nvarchar(4000)

-- initialization
if left(@smtp,2)=''##'' and right(@smtp,2)=''##''
    begin
    select @sql=''select @smtp=convert(sysname,dbo.fn__config(''''''
               +substring(@smtp,3,len(@smtp)-4)+'''''',null))''
    if @dbg=1 exec sp__printf ''-- %s'',@sql
    exec sp_executesql @sql,''@smtp sysname out'',@smtp=@smtp out
    end
else
    begin
    if left(@smtp,1)=''#'' and right(@smtp,1)=''#''
        begin
        select @sql=''select @smtp=convert(sysname,dbo.fn_config(''''''
                   +substring(@smtp,2,len(@smtp)-2)+'''''',null))''
        if @dbg=1 exec sp__printf ''-- %s'',@sql
        exec sp_executesql @sql,''@smtp sysname out'',@smtp=@smtp out
        end
    end

select
    @jname=''process montor ''+dbo.fn__hex(dbo.fn__crc32(@to)),
    @h=isnull(@h,2),
    @at=convert(sysname,@h)+''h'',
    @mins=@h*60,
    @sp=''exec sp__job_status @mins=''+convert(sysname,@mins)+
        '',@jobs=''''''+coalesce(@jobs,''%'')+''''''''+
        coalesce('',@excludes=''''''+@excludes+'''''''','''')+
        '',@to=''''''+@to+''''''''+
        '',@body=''''report jobs of:''+@@servername+''''''''+
        '',@smtp=''''''+@smtp+''''''''
-- ===================================================================== body ==
if @dbg=1 exec sp__printf ''sp:%s'',@sp
exec sp__job
    @jname,
    @sp=@sp,
    @at=@at,
    @opt=''sql''

goto ret

-- =================================================================== errors ==

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
scope
    install a job that monitor other processes

parameters
    @to     email;email;email which send list of failures
    @smtp   smtp server; a special syntax can be used here:
                ##var_name##    read value using "fn__config(''''var_name'''',null)"
                #var_name#      read value using "fn_config(''''var_name'''',null)"
    @jobs   by default monitor all jobs but can specify
            xxx|yyy|... to monitor all jobs that start with xxx or yyy
    @excludes   to excludes jobs that start with xxx or yyy
    @h      by default is every 2 hours, the slot time to monitor

notes
    the process monitor will have name "process monitor xxx"
    where xxx is a crc code of @emails.

examples
    exec sp__job_monitor ''''stezagl@tin.it'''',''''lupin''''
    exec sp__job_monitor ''''stefano.zaglio@seltris.it'''',''''lupin''''
    exec sp__job ''''process monitor#'''',# -- delete all monitors
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__job_monitor'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__job_monitor:

-- =============================================================== sp__job_setup
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__job_setup',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110305
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__job_setup') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__job_setup') with nowait
        goto skip_sp__job_setup
        end
    if @ver>110305
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__job_setup') with nowait
        goto skip_sp__job_setup
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__job_setup') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__job_setup'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__job_setup]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,utijob
    v:110305\s.zaglio:populate DB jobs from a local app table
    t:sp__job_setup @dbg=1
    t:sp__job_setup ''%'',@opt=''dis'',@dbg=1
    t:sp__job_setup ''%'',@tt=''%db_name%|db1|a|utility|u|db3|c'',@dbg=1
    t:sp__job_setup ''%'',@opt=''run|dis'',@dbg=1
*/
CREATE proc sp__job_setup
    @like   sysname=null,
    @root   sysname=null,
    @tt     nvarchar(4000)=null,
    @opt    sysname=null,
    @dbg    int=0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname,  @ret int -- standard API: 0=OK -1=HELP, any=error id
declare @tbl sysname
select  @proc=object_name(@@procid), @ret=0,
        @opt=dbo.fn__str_quote(isnull(@opt,''''),''|''),
        @tbl=convert(sysname,dbo.fn__config(''jobs_setup_table'',''tbl_jobs_setup''))

-- ========================================================= param formal chk ==
if (@like is null and @opt=''||'') or object_id(@tbl) is null goto help

-- ============================================================== declaration ==
declare
    @emails sysname,@smtp sysname,@at sysname,@sql nvarchar(4000),
    @ttv sysname,@i int,@job sysname,@sp sysname,@db sysname,
    @run bit,@crlf nvarchar(2)
declare @ttt table(tkn sysname,val sysname)

-- =========================================================== initialization ==
select top 1
    @run=charindex(''|run|'',@opt),
    @i=charindex(''|'',@tt),@db=db_name(),
    @emails=convert(nvarchar(4000),dbo.fn__config(''job_setup_emails'',null)),
    @smtp=convert(sysname,dbo.fn__config(''job_setup_smtp'',
                                         dbo.fn__config(''smtp_server'',null))),
    @crlf=crlf
from dbo.fn__sym()

-- sp__config ''job%'', sp__config ''job_setup_emails'','''',''see sp__job_setup''
-- sp__config ''job_setup_emails'',@del=1
if dbo.fn__chk_email(@emails,default)=0 goto err_email

select @ttv=left(@tt,@i-1)
select @ttv=replace(@ttv,''%db_name%'',@db)
select @ttv=convert(sysname,dbo.fn__config(@ttv,@ttv))

insert @ttt
select vt.token,vs.token
from (select * from dbo.fn__str_table(substring(@tt,@i+1,4000),''|'') vt
      where vt.pos%2=1) vt
join (select vs.pos-1 pos,token from dbo.fn__str_table(substring(@tt,@i+1,4000),''|'') vs
      where vs.pos%2=0) vs
on vt.pos=vs.pos

if @dbg=1
    begin
    select * from @ttt
    exec sp__printf ''-- @ttv=%s, @i=%s'',@ttv,@i
    end
select @ttv=val from @ttt where tkn=@ttv
if @dbg=1 exec sp__printf ''-- @ttv=%s'',@ttv

-- ======================================================== second params chk ==

-- ===================================================================== body ==
-- initialization

create table #jobs(
    id      int,
    rif     sysname,    -- 3 chars for person
    job     sysname,    -- @root+...
    rs      sysname,    -- filter system in multi system env.
    sp      sysname,    -- stored proc name
    sched   sysname,    -- HH:MM  or HHs:MMs Nm HHe:MMe
    opt     sysname null,
    email   sysname null,
    )

exec(''insert into #jobs select * from [''+@tbl+'']'')
if @@error!=0 goto err_tbl

update #jobs set opt=dbo.fn__str_quote(isnull(opt,''''),''|'')
if charindex(''|dis|'',@opt)>0
    update #jobs set opt=opt+''dis|''

if @dbg=1 select * from #jobs

-- ===================================================================== body ==

select
    s.id,
    isnull(@root+''_'','''')+job as job,
    isnull(
        replace(
            sp,
            ''%ttv%'',
            isnull((select tkn from @ttt where val=@ttv),@db)
            ) -- replace
        ,sp -- is null
        ) sp,
    sched, dbo.fn__str_quote(opt,''|'') opt
into #steps
from #jobs s
where dbo.fn__str_in(@ttv,s.rs,'''')=1
and (@like is null or job like @like)

update steps set
    sp=@db+''.dbo.''+sp
from #steps steps
where charindex(''|sql|'',opt)=0
and left(sp,len(@db))!=@db

-- check for existance of sp (tbl_jobs_setup)
if @dbg=1 exec sp__printf ''--test existances''
select sp as sp_unk_name into #unksp from #steps
where charindex(''|sql|'',opt)=0
and object_id(sp) is null

if exists(select null from #unksp)
    begin
    exec sp__printf ''-- list of missed sp''
    exec sp__select_astext ''select * from #unksp'',@header=1
    drop table #unksp
    goto ret
    end
drop table #unksp

if @dbg=1 select * from #steps order by id

if @run=0
    exec sp__printf ''emails status to:%s\nsmtp:%s'',@emails,@smtp

-- delete jobs
if isnull(@root,'''')!=''''
    begin
    select @job=@root+''_''+isnull(@like,'''')+''#''
    exec sp__printf ''deleting "%s" jobs'',@job
    if @run=1 exec sp__job @job,#
    end

if isnull(@root,'''')='''' and charindex(''|add|'',@opt)=0
    begin
    declare cs cursor local for
        select distinct job
        from #steps
    open cs
    while 1=1
        begin
        fetch next from cs into @job
        if @@fetch_status!=0 break

        select @sql=@job+''#''
        exec sp__printf ''deleting "%s" steps'',@sql
        if @run=1 exec sp__job @sql,#
        end
    close cs
    deallocate cs
    end -- delete jobs

-- for each system & step of job
declare cs cursor local for
    select job,sp,sched,opt
    from #steps
    order by id
open cs
while 1=1
    begin
    fetch next from cs into @job,@sp,@at,@opt
    if @@fetch_status!=0 break

    if @run=0 or @dbg=1
        begin
        select @sql=''exec sp__job\n\t@job="%s",\n\t@sp="%s",\n\t@at="%s",\n\t@opt="%s"\n\t''+
                    ''@emails="%s",\n\t@smtp="%s"''
        select @sql=replace(@sql,''\n\t'',@crlf)
        select @sql=replace(@sql,''"'','''''''')
        exec sp__printf @sql,@job,@sp,@at,@opt,@emails,@smtp
        end

    if @run=1
        exec sp__job @job=@job,@sp=@sp,@at=@at,
                     @emails=@emails,@smtp=@smtp,
                     @opt=@opt

    end -- while of cursor
close cs
deallocate cs

goto ret

-- =================================================================== errors ==
err_tbl:    exec @ret=sp__err ''uncomplete of bad structure of setup table. See help'',@proc
            goto ret
err_email:  exec @ret=sp__err ''bad or null email:"%s". See help'',@proc,@p1=@emails
            goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    generate and regenerate jobs from ##jobs_setup_table##
    (by default "tbl_jobs_setup").

Notes
    the JOB and SP fields are the key for single step update;
    cfg "job_setup_emails,job_setup_smtp,smtp_server" are used
    for general emails

Parameters
    @root   is the brief name of the application
    @like   recreate only jobs where name like @like
    @tt     is the translate table to match TTV field (see example)
            accept macros: %db_name%, any ##cfg## or #cfg# (see sp__config)
    @opt    options
            run     execute it
            add     do not delete jobs, add or replace only
            dis     disabled to each added job
    @dbg    debug mode, 1 show info

Jobs table

    create table tbl_jobs_setup (
        id      int,            -- give the order of steps
        rif     sysname,        -- 3 chars for person
        job     sysname,        -- @root+this become the name of job
        ttv     sysname,        -- match the @tt (see example)
        sp      sysname,        -- stored proc name (%ttv% is replaced with val in @tt)
        sched   sysname,        -- HH:MM  or HHs:MMs Nm HHe:MMe
        opt     sysname null,   -- extra option passed to sp__job
        email   sysname null    -- alternative email for this step
        )

Utilities

    insert tbl_jobs_setup(id, grp, rif, job, rs, sp, sched)
    select
        id,
        grp,
        rif,
        job_Ext,
        rs,
        sp,
        sched

    update tbl_jobs_setup set
        sched=
    where id=???

    delete from tbl_jobs_setup where job=''''???''''


    insert tbl_jobs_setup(
                  id, rif,    job,              ttv,            sp,                             sched,              opt
            )
    --           --- ---------- ------------------ ----------------- ------------------------------ --------------------- -------------
          select  10, ''''me'''',   ''''%job1%'''',        ''''*'''',            ''''%sp1%'''',                     ''''%sched1%'''',         ''''%opt1%''''
    union select  20, ''''you'''',  ''''%job2%'''',        ''''*'''',            ''''%sp2%'''',                        ''''%sched1%'''',         null

Examples
    sp__job_setup @tt=''''%db_name%|db1|a|utility|u|db3|c'''',@dbg=1
    -- replace macro with current db_name() and if this match "db1",
    -- rows where ttv contains "a" are selected

''
if @dbg=1 exec sp__printf ''-- @tbl=%s'',@tbl
if not object_id(@tbl) is null
    begin
    select @sql=''select * from [''+@tbl+''] order by 1''
    exec sp__select_astext @sql
    end

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__job_setup'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__job_setup:

-- ============================================================== sp__job_status
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__job_status',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140103
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__job_status') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__job_status') with nowait
        goto skip_sp__job_status
        end
    if @ver>140103
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__job_status') with nowait
        goto skip_sp__job_status
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__job_status') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__job_status'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__job_status]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:140103\s.zaglio: around backward compatibility near @mins=-1
    v:131223\s.zaglio: added send of email even if problem with attachment
    v:131129\s.zaglio: wait more time for problem of attachment locked
    r:131127.1100\s.zaglio: some adjustements about parameters and correct help
    r:131125\s.zaglio: integrating fn__job_Status to send emails every 4,16,64,256
    r:131122\s.zaglio: integrating fn__job_Status and fn__config_app
    r:131117\s.zaglio: pretty prints and removed sp4,8; adapting to fn__job_status
    r:131116\s.zaglio: set @mins to -1 when @step specified and added try-catch
    v:130409\s.zaglio: when @mins<0 unqueue sp4/sp8 options
    v:121210\s.zaglio: a bug cause by sp__select_astext
    v:120726\s.zaglio: added @header=1 on sp__select_astext
    v:111130\s.zaglio: body into html and added ref and last_upd
    v:110715\s.zaglio: better messages
    v:110212\s.zaglio: better help
    v:100927.1010\s.zaglio: moved @mins parameter and a bug near list and @mins=-1
    v:100926\s.zaglio: added with (nolock) and -1 on multi job
    v:100919.1100\s.zaglio: added log in opt to print txts (autoprinted if step is 1)
    v:100919.1000\s.zaglio: reordered params and added opt
    v:100919\s.zaglio: added slot management
    v:100724.1000\s.zaglio: a bug near -1 (last error) and calc when @mins>0
    v:100626\s.zaglio: a bug near time
    v:100625\s.zaglio: a little bug and optimization
    v:100615\s.zaglio: added -1 option and a bug near selection
    v:100612\s.zaglio: done and tested
    r:100424\s.zaglio: send report of failed job of last hour
    t:sp__job_status ''%status%'',''raiserror'',@opt=''log''
    t:sp__job_status ''%status%'',''raiserror'',@opt=''log|sel''
    t:sp__job_status ''%status%'',@mins=2440,@opt=''log''
    t:update cfg set val=''X'' where [key]=''job_status.test''
    t:update cfg set val='' '' where [key]=''job_status.test''
    t:sp__job ''sp__job_status_test'',@opt=''run''
    t:sp__job_status ''%'',@opt=''sel''
    t:sp__job_status ''%'',@opt=''sel'',@mins=2880
    t:select * from fn__job_status(''%status%'',default,''fle'')
    t:msdb..sp_help_jobhistory @job_name=''sp__job_status_test''
    t:sp__job_status ''%status%'',@mins=-1,@to=''*''
*/
CREATE proc [dbo].[sp__job_status]
    @jobs nvarchar(4000) = null,    -- filters for name%|name%|...
    @steps nvarchar(4000) = null,   -- filters for step|step|...
    @mins int     = null,           -- failed of lasts minutes or job name
    @excludes nvarchar(4000) = null,
    @attach bit = null,
    @to nvarchar(4000) = null,      -- send status to this email
    @body nvarchar(4000) = null,
    @smtp sysname = null,
    @ref sysname = null,
    @opt sysname = null,
    @dbg bit = 0
as
begin try
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0,
       @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

declare @job_id uniqueidentifier

select @job_id=job_id,@jobs=isnull(name,@jobs) from fn__job(@@spid)

if @jobs is null goto help

if @mins<-1 or @mins=0 goto help

-- filters
declare @filters table (id int identity,tid tinyint,dat sysname)

if @jobs is null
    insert @filters select 1,''%''

if @steps is null
    insert @filters select 2,''%''
else
    begin
    insert @filters
    select 2,token
    from dbo.fn__str_table(@steps,''|'')
    end

if not @excludes is null
    insert @filters
    select 3,''%''+token+''%''
    from dbo.fn__str_table(@excludes,''|'')

-- ==================================================== variable declarations ==

declare
    @previousdate datetime,
    @id int,@hh int, @n int,@i int,
    @logfile nvarchar(512),@cmd nvarchar(4000),
    @header int,
    @sql nvarchar(4000),
    @attaches nvarchar(4000),
    -- options
    @log bit,@ok bit,@sel bit,@nep bit,@err bit,@back bit,
    @fn_opt sysname

create table #src(lno int identity,line nvarchar(4000))

-- ===================================================================== init ==

select
    @attach=isnull(@attach,1),
    @back=case @mins when -1 then 1 else 0 end,
    @mins=nullif(@mins,-1)

if @mins is null
    select @mins=7*24*60,@fn_opt=''fle''

select
    @to=nullif(@to,''''),
    @smtp=nullif(@smtp,''''),
    @ok=charindex(''|ok|'',@opt),
    @sel=charindex(''|sel|'',@opt),
    @log=charindex(''|log|'',@opt),
    @nep=charindex(''|nep|'',@opt)

-- ===================================================================== body ==

if not @job_id is null or @to=''*''
    begin
    select @to=nullif(@to,''*'')

    if @to is null
        select @to=isnull(@to,cast(
                    dbo.fn__config_app(''.SUPPORT_EMAIL|job_status.to'',default)
                    as nvarchar(4000)
                    ))
    if @smtp is null
        select @smtp=isnull(@smtp,cast(
                        dbo.fn__config_app(''.MAIL_SMTP_SERVER|''+
                                           ''job_status.smtp|smtp_server'',default)
                        as nvarchar(4000)
                        ))

    if @to is null or @smtp is null
        raiserror(''@smtp or @to not specified'',16,1)

    exec sp__printf ''-- auto job idf:%s to:%s(%s)'',@jobs,@to,@smtp
    end

if not @job_id is null
    begin

    -- prevent continuos resend of message error
    if @nep=0
        begin
        select top 1 @err=err,@n=n
        from fn__job_status(@jobs,default,''fle'')
        where id=@job_id
        if @err=1
            begin
            if @n>4 and not @n in (16,64,128,256)
                begin
                exec sp__printf ''-- skipped because more than 4 errors''
                goto ret
                end
            end
        else
            goto ret
        end
    -- select @mins=-1 this do not work properly
    end -- auto job determination

-- ====================================================== read all job status ==

select
    h.server,
    s.database_name as db,
    fjn.[name] as job,
    h.step_id as sid,
    case when h.sql_severity>10 then ''X'' else '''' end S,
    dbo.fn__str_pad(convert(sysname,h.run_date),8,default,default,default)
    as run_date,
    dbo.fn__str_pad(convert(sysname,h.run_time),6,default,default,default)
    as run_time,
    fjn.n as n,
    h.run_duration secs,
    s.step_name as step,
    isnull((select
            convert(sysname,si.val1)+''\''+convert(sysname,si.val2)
            from dbo.fn__script_info(s.command,''vr'',0) si
            ),replace(replace(replace(
              left(s.command,40)+case when len(s.command)>40 then ''...'' else '''' end,
              char(13),'' ''),char(10),'' ''),''  '','' '')
        )
    as last_upd,
    -- h.sql_severity,
    left(''..''+
    replace(replace(
    replace(replace(
    replace(replace(
    replace(replace(
    replace(replace(
    h.message,
    ''[SQLSTATE 01000]'',''''),''(Message 50000)'',''''),
    ''[SQLSTATE 42000]'',''''),''(Error 50000)'',''''),
    char(13),'' ''),char(10),'' ''),
    ''..'',''''),''The step '',''''),
    ''  '','' ''),''(Message 0)'','''')
    ,128) as [message],
    s.output_file_name

into #failures

from fn__job_status(@jobs,@mins,@fn_opt) fjn
join msdb.dbo.sysjobhistory h with (nolock)
  on h.job_id=fjn.id
 and h.run_date=fjn.run_date
 and h.run_time between fjn.from_time and fjn.run_time
join msdb.dbo.sysjobsteps s with (nolock)
  on h.job_id = s.job_id and h.step_id=s.step_id
join @filters flts on flts.tid=2 and s.step_name like flts.dat
left join @filters fltexj on fltexj.tid=3 and h.message like fltexj.dat
where charindex(''exec sp__job_status'',s.command)=0
and left(s.command,14)!=''sp__job_status''
and fltexj.id is null
order by h.run_date desc,server,row desc,h.run_time desc,h.step_id desc

-- ================================================================= @mins=-1 ==

-- if calling from job...
if not @job_id is null or @back=1
    begin
    delete f
    from #failures f
    left join (
        select top 1 *
        from #failures
        order by run_date desc,run_time desc,sid desc
        ) ff
    on ff.run_date=f.run_date and ff.run_time=f.run_time and ff.sid=f.sid
    where ff.sid is null

    if ''X''!=(
        select top 1 s
        from #failures
        order by run_date desc,run_time desc,sid desc
        )
        goto ret

    end -- from job

if @attach=1
    begin
    /*  111130\s.zaglio: converted into html-body
        select @attaches=''select * from #failures order by instance_id desc'' */
    select @attaches=coalesce(@attaches+'';'','''')+output_file_name
    from (
        -- optimize files for more steps
        select distinct output_file_name
        from #failures
        where coalesce(output_file_name,'''')!=''''
        ) sq

    if @dbg=1 exec sp__printf ''-- %s:attaches:%s'',@proc,@attaches
    end

select @n=count(*) from #failures
if @n>0
    begin
    -- sp__job_status @mins=-1,@jobs=''spj_test'',@opt=''ok'',@dbg=1
    if @dbg=1 exec sp__printf ''-- %s:%d failed job steps found for jobs %s'',
                              @proc,@n,@jobs
    if not @to is null
        begin

        insert #src select ''<html><body>''
        insert #src select ''jobs:''+@jobs

        if not @body is null insert #src select @body

        exec sp__select_astext ''
        select
            SERVER,DB,JOB,SID,S,RUN_DATE,RUN_TIME,N,STEP,LAST_UPD,
            MESSAGE
        from #failures
        order by run_date desc,run_time desc,sid desc
        '',
        @out=''#src'',@opt=''html'',@header=1,@dbg=0

        insert #src select ''</body></html>''

        declare @subj sysname
        select @subj=quotename(dbo.fn__servername(null))
                    +''.sp__job_status failed steps for jobs: ''
                    +coalesce(@jobs,''ALL'')

        -- retry because some time the agent block the log
        select @n=1
        while @n<4
            begin try
            exec sp__printf ''-- send email (%d)'',@n
            exec sp__email
                @to=@to,
                @subj=@subj,
                @body=''#src'',
                @attach=@attaches,
                @smtp=@smtp,
                @dbg=@dbg
            break
            end try
            begin catch
            select @n=@n+1
            if @n=4
                begin
                select @sql=error_message()
                exec sp__printf ''%s'',@sql
                insert #src select ''<br><font color="#FF0000">''+@sql+''<br>''
                insert #src select @attaches+''</font><br>''
                exec sp__email
                    @to=@to,
                    @subj=@subj,
                    @body=''#src'',
                    @smtp=@smtp,
                    @dbg=@dbg
                end
            else
                waitfor delay ''00:00:02''
            end catch

        end -- @n>0

    else

        begin
        -- show only to console
        if @ok=0
        -- and charindex(''|log|'',@opt)=0
        --or ((select count(*) from #failures)>1
            begin
            -- then show steps with log
            select @sql=''
                select * from #failures
                order by run_date desc,run_time desc,sid desc
                ''
            if @n>20 select @header=2 else select @header=1
            if @sel=1 exec(@sql)
            else exec sp__select_astext @sql,@header=@header
            end

        -- ##########################
        -- ##
        -- ## print log
        -- ##
        -- ########################################################

        if @log=1
            begin
            -- first show steps without log
            if exists(
                select top 1 null from #failures
                where isnull(output_file_name,'''')=''''
                )
                begin
                select @sql=''
                    select ''''step without log'''' info,* from #failures
                    where isnull(output_file_name,'''''''')=''''''''
                    order by run_date desc,run_time desc,sid desc
                    ''

                if @sel=1 exec(@sql)
                else
                    begin
                    exec sp__printf ''''
                    exec sp__select_astext @sql,@header=1
                    end
                end -- steps without log

            -- sp__job_status ''%status%'',@opt=''log''
            declare cs cursor local for
                select output_file_name
                from #failures
                order by run_date desc,run_time desc,sid desc
            open cs
            while 1=1
                begin
                fetch next from cs into @logfile
                if @@fetch_status!=0 break

                if not isnull(@logfile,'''')=''''
                    begin
                    select @cmd=''type "''+@logfile+''"''
                    truncate table #src
                    insert #src exec master..xp_cmdshell @cmd
                    update #src set line=
                        replace(replace(
                        replace(replace(
                            line,
                        ''[SQLSTATE 01000]'',''''),''(Message 50000)'',''''),
                        ''[SQLSTATE 42000]'',''''),''(Error 50000)'','''')

                    exec sp__printf ''''
                    exec sp__prints @logfile
                    if @sel=1
                        exec(''select line [''+@logfile+''] from #src order by lno'')
                    else
                        exec sp__print_table ''#src''
                    -- exec sp__select_astext ''select * from #src order by 1'',@header=0
                    end -- print log

                end -- cursor cs
            close cs
            deallocate cs
            end -- print log

        end -- print failed step info

    end -- if there is history
else
    exec sp__printf ''-- %s:no wrong step found for jobs %s in the lasts %d mins'',
                    @proc,@jobs,@mins

dispose:
drop table #failures
drop table #src

goto ret

-- =================================================================== errors ==

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    print or send a report about jobs

Notes
    - can be added as last step of a job
    - %obj% can automatically detect running job
    - %obj% uses fn__config_app to get values for @smtp and @to

Parameters
    @jobs       filter jobs (job1|job2|...) (default is %)
    @steps      filter steps (step1|step2|...) (default is %)
    @mins       is the number of minutes before now to watch (default is -1)
                -1 means from last error, if last run was wrong
                1440 means last 24h
                2880 means last two days
    @excludes   filter jobs for exclusions (job1|job2|...)
    @attach     attach output files with message (default=1)
    @to         emails to sent report
                if null or empty will be searched:
                    1st in application config: SUPPORT_EMAIL
                    2nd in utility config: job_status.to
    @body       optional email message text
    @smtp       smtp server name or ip or
                if null or empty will be searched:
                    1st in application config: MAIL_SMTP_SERVER
                    2nd in utility config: job_status.smtp
                    3rd in utility config: smtp_server
    @ref        system or person reference for step
                in case of stored proc., the column last_upd report the date and name
                of last that has changed the code
    @opt        options
                log     show the txt of log
                sel     show results as select
                ok      show also not failed lines
                nep     do not apply continuous error prevention

Examples
    sp__job_status                                  -- show this help and jobs with failed steps

    sp__job_status "%status%"                       -- show info about jobs that contain "status"
                                                    -- and limited to last failure to now
    sp__job_status "%status%",@mins=2880            -- show info about jobs that contain "status"
                                                    -- of lasts two days
    sp__job_status "%status%",@opt="log"            -- show info about failed steps with logs (.txt)
    sp__job_status "%status%",@opt="ok|log"         -- show info about all steps of jobs (with .txt)
    sp__job_status "%",@opt="sel|ok|log"            -- show all steps as select
''
select
    job_failed as [job_failed(jf)],nsf,
    substring(convert(sysname,last_run),1,8) last_datef,
    substring(convert(sysname,last_run),9,6) last_timef
into #sp__job_status_list
from (
    select
        [name] as job_failed,
        (select count(*) from msdb..sysjobhistory h with (nolock)
         where h.job_id=j.job_id and h.run_status = 0 and h.sql_severity>0)
        as nsf,
        (select max(convert(bigint,h.run_date)*1000000+h.run_time) as last_run
         from msdb..sysjobhistory h with (nolock)
         where h.job_id=j.job_id and h.run_status = 0 and h.sql_severity>0)
        as last_run
    from msdb..sysjobs j with (nolock)
    ) j
where nsf>0
if @@rowcount>0
    select
        *,
        case when last_datef=convert(sysname,getdate(),112) then ''*'' else '''' end
        as [@]
    from #sp__job_Status_list
    order by last_datef desc,last_timef desc
drop table #sp__job_status_list
select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- sp__job_status'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__job_status:

-- ================================================================ sp__job_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__job_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140103
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__job_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__job_test') with nowait
        goto skip_sp__job_test
        end
    if @ver>140103
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__job_test') with nowait
        goto skip_sp__job_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__job_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__job_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__job_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:job,test,auto,find,name,running
    v:140103\s.zaglio: callable from sp__utility_setup
    v:130209\s.zaglio: test func fn__job
    t:sp__job_test @dbg=1
*/
CREATE proc sp__job_test
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @run bit,@setup bit,
    @sp sysname

-- =========================================================== initialization ==
select @run=charindex(''|run|'',@opt),@setup=charindex(''|setup|'',@opt)

-- ======================================================== second params chk ==

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

if @run=0
    begin
    select @sp=@proc+'' @opt=''''run''''''
    -- delete prev job
    exec sp__job @proc,''#''
    exec sp__job @proc,@sp,@opt=''sql|run|nolog''
    -- exec sp__job_wait @proc
    waitfor delay ''00:00:01''
    if @dbg=0 exec sp__job @proc,''#''
    if object_id(''tmp_sp__job_test'') is null raiserror(''test failed'',16,1)
    if @setup=0 select * from tmp_sp__job_test
    drop table tmp_sp__job_test
    end
else
    begin
    if not object_id(''tmp_sp__job_test'') is null drop table tmp_sp__job_test
    create table tmp_sp__job_test(
        job_id sql_variant null,name sql_variant null,
        loginname sql_variant null,login sql_variant null,
        ologin sql_variant null,ja sql_variant null
        )
    insert into tmp_sp__job_test(job_id,name,loginname,ja)
    select *,dbo.fn__job_agent()
    from fn__job(@@spid)
    end

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

if @setup=1 goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test if fn__job works in this system;
    if it do not work, the sp__job_status and all the SP called from a job,
    do not understand that they are running into a job.

Parameters
    [param]     [desc]
    @opt        options
                run     run the test (called by itself from a temporary job)
                setup   when called by sp__utility_setup
    @dbg        debug info
                1   do not delete temporary job

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__job_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__job_test:

-- ================================================================ sp__job_wait
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__job_wait',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090504
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__job_wait') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__job_wait') with nowait
        goto skip_sp__job_wait
        end
    if @ver>090504
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__job_wait') with nowait
        goto skip_sp__job_wait
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__job_wait') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__job_wait'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__job_wait]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090504\S.Zaglio: from http://www.sqlservercentral.com/scripts/Miscellaneous/30124/
    t: to study and integrate into sp__job
*/
CREATE PROCEDURE [sp__job_wait]
    @jobs nvarchar(2000),
    @is_running bit = 0,
    @max_seconds int = 10,
    @pause_seconds int = null,
    @retrying_equals_running bit = 0
AS
/*
    Procedure waits until the indicated job or jobs, have the status of running or stopped.
    It will wait until it has reached the max wait time, checking the status every 5 seconds.

    It returns the results of the job.

     1 - successfull
     2 - retrying
     3 - cancelled
    -1 - not started
    -2 - executing
     0 - failed
*/
declare
    @job nvarchar(50),
    @b int,
    @e int,
    @running int,
    @result int,
    @tries int,
      @is_sysadmin INT,
    @job_owner   sysname,
    @pause_string nvarchar(9),
    @pause_number int,
    @max_tries int,

    @last_run_date int,
    @last_run_time int,
    @run_date int,
    @run_time int,

    @minutes int,
    @hours int,
    @seconds int
begin

    set nocount on

    set @jobs = rtrim(ltrim(@jobs))

    if right(@jobs,1)<>'',''
    begin
        set @jobs = @jobs + '',''
    end

    -- create temp tables
    create table #wait_job_status (
        job_id uniqueidentifier not null,
        last_run_date int not null,
        last_run_time int not null,
        next_run_date int not null,
        next_run_time int not null,
        next_run_schedule_id int not null,
        requested_to_run int not null, -- BOOL
        request_source int not null,
        request_source_id sysname null,
        running int not null, -- BOOL
        current_step int not null,
        current_retry_attempt int not null,
        job_state int not null
    )

    create table #wait_job_list (
        [name] nvarchar(100) not null primary key
    )

    -- set variables for status
    set @is_sysadmin = ISNULL(IS_SRVROLEMEMBER(N''sysadmin''), 0)
    set @job_owner = SUSER_SNAME()

    if isnull(@pause_seconds,0)<1
    begin
        set @pause_number = floor(@max_seconds / (case when @max_seconds>30 then 4 else 2 end))
        set @pause_number = case when @pause_number > 15 then 15 when @pause_number < 1 then 1 else @pause_number end
    end
    else
    begin
        set @pause_number = @pause_seconds
    end

    select
        @hours = floor(@pause_number / 3600),
        @minutes = floor((@pause_number - (@hours * 3600)) / 60),
        @seconds = (@pause_number - (@hours * 3600) - (@minutes * 60)),
        @pause_string = right(''00''+convert(nvarchar,@hours),2)+'':''+
                        right(''00''+convert(nvarchar,@minutes),2)+'':''+
                        right(''00''+convert(nvarchar,@seconds),2)

    set @max_tries = @max_seconds / @pause_number

    set @b = 0
    set @e = CHARINDEX('','',@jobs,@b+1)

    while (@e>0)
    begin
        set @job = SUBSTRING(@jobs,@b+1,@e-(@b+1))

        insert into #wait_job_list([name]) values(lower(ltrim(rtrim(@job))))

        set @b = @e
        set @e = CHARINDEX('','',@jobs,@b+1)
    end

    -- begin status check
    set @running = case when @is_running=1 then 0 else 1 end
    set @tries = 0
    set @last_run_date=0
    set @last_run_time=0
    set @run_date=0
    set @run_time=0

    while @running= case
                    when @is_running=1 then 0
                    else 1 end
          and @tries<=@max_tries and (@last_run_date=@run_date)
          and (@last_run_time=@run_time)
    begin
        set @running = @is_running
        set @tries = @tries + 1
        set @result = 0

        truncate table #wait_job_status

        insert into #wait_job_status
        exec master..xp_sqlagent_enum_jobs @is_sysadmin, @job_owner

        select
            @running = case when @is_running = 1 then 0 else 1 end,
            @last_run_date = case when @tries=1 then x.last_run_date else @last_run_date end,
            @last_run_time = case when @tries=1 then x.last_run_time else @last_run_time end,
            @run_date = x.last_run_date,
            @run_time = x.last_run_time
        from
            #wait_job_status x
        inner join
            msdb..sysjobs s
        on
            x.job_id=s.job_id
        left join
            msdb..sysjobhistory h
        on
            h.job_id=x.job_id and h.step_id=0 and h.run_date=x.last_run_date and h.run_time=x.last_run_time
        where
            lower(s.[name]) in (select [name] from #wait_job_list) and
            case when
            (@is_running=1 and (x.[running]<1 or (x.[running]>0 and x.[job_state]<>1 and @retrying_equals_running=0))) or
            (@is_running=0 and x.[running]>0 and (x.[job_state]=1 or @retrying_equals_running=1))
            then 1 else 0 end = 1

        if (@running=case when @is_running=1 then 0 else 1 end) and (@last_run_date=@run_date) and (@last_run_time=@run_time)
        begin
            -- job (or jobs) are not in correct state, so wait - then try again
            waitfor delay @pause_string
        end
        else
        begin
            -- job is in correct state, so pull status
            select
                @result = case when x.[job_state]<>1 and x.[running]=1 then 2 else h.[run_status] end
            from
                #wait_job_status x
            inner join
                msdb..sysjobs s
            on
                x.job_id=s.job_id
            left join
                msdb..sysjobhistory h
            on
                h.job_id=x.job_id and h.step_id=0 and h.run_date=x.last_run_date and h.run_time=x.last_run_time
            where
                lower(s.[name]) in (select [name] from #wait_job_list)
            order by
                case h.[run_status]
                    when 3 then 2
                    when 1 then 3
                    else 1 end desc
        end
    end

    if @tries>@max_tries
    begin
        select @result = case when @is_running=1 then -1
            else -2 end
    end

    -- drop temp tables
    drop table #wait_job_status
    drop table #wait_job_list

    -- return results
    return @result

end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__job_wait:

-- ==================================================================== sp__link
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__link',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090517
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__link') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__link') with nowait
        goto skip_sp__link
        end
    if @ver>090517
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__link') with nowait
        goto skip_sp__link
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__link') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__link'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__link]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090517\S.Zaglio: create a link to a remote object
*/
create proc sp__link @obj sysname,@server sysname, @db sysname=null, @uid sysname=''sa'', @pwd sysname='''',@drop bit=0,@dbg bit=0
as
begin
set nocount on
declare @wid sysname select @wid=''''
declare @sql nvarchar(4000)

if @db is null select @db=db_name()
select @sql =''create view [''+@obj+ ''] as ''
            +''select * from opendatasource(''''SQLOLEDB'''',''
            +''''''''
            +''Persist Security Info=True;UID=''+@uid+'';''
            +''Initial Catalog=''+@db+'';''
            +''SERVER=''+@server+'';''
            +''Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;''
            +''Workstation ID=''+@wid
            +''Use Encryption for Data=False;Tag with column collation when possible=False''
            +''''''''
            +'').''+@db+''.dbo.[''+@obj+''] rowset_1''

if @dbg=1 print @sql
if @drop=1 exec sp__drop @obj
exec(@sql)
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__link:

-- ==================================================================== sp__lock
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__lock',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120905
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__lock') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__lock') with nowait
        goto skip_sp__lock
        end
    if @ver>120905
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__lock') with nowait
        goto skip_sp__lock
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__lock') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__lock'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__lock]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120905\s.zaglio: moved out the help
    v:120827\s.zaglio: a small bug
    v:101211\s.zaglio: replace call of sp_lock
    v:100919\s.zaglio: added WITH NO_INFOMSGS to not show dbcc out
    v:100615\s.zaglio: added with nolock everywhare
    v:091221\s.zaglio: a different approach because alter table on # don''t work. Better use select into
    v:091123\s.zaglio: replaced use of object_name because is blocked by transaction and not influenced by set trans...
    v:091116\s.zaglio: help find locks (replace also old sp__find_root_blocker)
    s:sp__lock_ex
    t:sp__lock @@spid
    t:sp__lock ''#lock''
    t:sp__lock @dbg=1
    t:print @@trancount
    t:sp__lock @blocking=1 -- sp__lock 7 -- sp_lock
    t:select * from master..sysprocesses where spid=7
    t:sp__run_cmd ''net start'' -- sp__run_cmd ''net stop "SQLAgent$WEBAPP" & net start "SQLAgent$WEBAPP"''
    t:sp__lock 70,@tsql=1
*/
CREATE proc [dbo].[sp__lock]
(
    @spid_obj_login_root sysname=null,
    @db sysname = null,
    @tsql bit=null,
    @blocking bit=0,
    @dbg bit=0
)
as
begin
-- declare @spid_obj_login_root sysname,@db sysname,@dbg bit set @dbg=1

set nocount on
set transaction isolation level read uncommitted
declare @spid int, @obj sysname, @name sysname,@proc sysname
declare @i int,@n int,@sql nvarchar(4000),@id int
declare @timer datetime

select @proc=object_name(@@procid)

declare @sysobjects table (id int, name sysname)

select @timer=current_timestamp

if @spid_obj_login_root is null exec sp__usage @proc
else select @tsql=1

select @tsql=coalesce(@tsql,0)


if isnumeric(@spid_obj_login_root)=1 select @spid=convert(int,@spid_obj_login_root)
else select @obj=@spid_obj_login_root+''%''

create table #lock1
(
    spid int,
    dbid int,
    objid int,
    indid int,
    type nchar(5),
    resource sysname,
    mode sysname,
    status sysname
)

-- select * from master..syslocks
exec sp__printf ''-- sp__lock:%T: read from sp_lock'',''%t''  -- ''%t'' otherwise do not print date
insert into #lock1
select
    convert (smallint, req_spid) As spid,
    rsc_dbid As [dbid],
    rsc_objid As [ObjId],
    rsc_indid As IndId,
    substring (v.name, 1, 4) As Type,
    substring (rsc_text, 1, 32) as Resource,
    substring (u.name, 1, 8) As Mode,
    substring (x.name, 1, 5) As Status
-- select *
from
    master.dbo.syslockinfo with (nolock),
    master.dbo.spt_values v with (nolock),
    master.dbo.spt_values x with (nolock),
    master.dbo.spt_values u with (nolock)
-- SELECT * FROM sys.dm_tran_locks
where master.dbo.syslockinfo.rsc_type = v.number
and v.type = ''LR''
and master.dbo.syslockinfo.req_status = x.number
and x.type = ''LS''
and master.dbo.syslockinfo.req_mode + 1 = u.number
and u.type = ''L''
order by spid

-- select * from master..sysprocesses p
select * into #processes from master..sysprocesses p with (nolock)
-- where p.status!=''sleeping'' ! nope coz can be active lock on (h)old spid

exec sp__printf ''-- sp__lock:%T: altering #lock'',''%t''  -- ''%t'' otherwise do not print date

select
    identity(int,1,1) as id,
    convert(sysname,null) as [db_name],
    convert(sysname,null) as obj_name,
    convert(sysname,null) as p_status,
    convert(sysname,null) as sql,
    convert(sysname,null) as login,
    convert(sysname,null) as host,
    convert(int,null) waittime,
    convert(int,null) open_trans,
    convert(datetime,null) last_batch,
    convert(int,null) blocking,
    convert(int,null) n_locks,
    #lock1.*
into #lock
from #lock1 with (nolock)

drop table #lock1
-- capture sql as first to not loose it

if @tsql=1
    begin
    create table #dbccout(language_event sysname,parameters INT,event_info NVARCHAR(4000))

    exec sp__printf ''-- sp__lock:%T: get cmds'',''%t''  -- ''%t'' otherwise do not print date

    select @i=min(id),@n=max(id) from #lock with (nolock)
    while (@i<=@n)
        begin
        select @id=spid from #lock with (nolock) where id=@i
        truncate table #dbccout

        select @sql=''insert into #dbccout exec(''''dbcc inputbuffer(''+convert(sysname,@id)+'') with no_infomsgs'''')''
        if @dbg=1 exec sp__printf @sql
        exec(@sql)
        update l set sql=left(ltrim(rtrim(d.event_info)),128)
        from #lock l with (nolock) join #dbccout d with (nolock) on l.spid=@id
        select @i=@i+1
        end -- while

    drop table #dbccout
    end -- @tsql

exec sp__printf ''-- sp__lock:%T: update other data'',''%t''  -- ''%t'' otherwise do not print date

-- update other data    (select * from #processes)
update l set
    host=p.hostname,waittime=p.waittime,
    p_status=p.status,
    [login]=p.loginame,
    open_trans=p.open_tran,last_batch=p.last_batch,
    blocking=p2.spid,
    n_locks=(select count(*) from #lock with (nolock) where #lock.spid=l.spid group by(spid))
from #lock l with (nolock)
join #processes p with (nolock) on l.spid=p.spid
left join #processes p2 with (nolock) on p.spid=p2.blocked

-- format 1st row to conditionate the output
/*
insert into #lock(spid,n_locks,
    [db_name],obj_name,[type],mode,[login],host,[sql])
select 0,999,
    ''-db_name-'',''-obj_name----'',''--'',''--'',''-login-'',''-host-'',''-sql----------------''
*/
exec sp__printf ''-- sp__lock:%T: complete db name and object name'',''%t''  -- ''%t'' otherwise do not print date

update l set [db_name]=d.name
from #lock l with (nolock) join master..sysdatabases d with (nolock)
on l.dbid=d.dbid

/*
declare @last_id int,@last_name sysname
select @i=min(id),@n=max(id) from #lock with (nolock)
while (@i<=@n)
    begin
    select @id=l.objid,@name=l.[db_name] from #lock l with (nolock) where id=@i
    if @last_id is null or @last_id!=@id
        begin
        select @sql=''select @name=name from ''+quotename(@name)+''..sysobjects with (nolock) where id=''+convert(sysname,@id)+''''
        if @dbg=1 exec sp__printf @sql
        select @name=@name+''(''+convert(sysname,@id)+'')''
        exec sp_executesql @sql,N''@name sysname out'',@name=@name out
        end
    update #lock set obj_name=@name where id=@i
    select @i=@i+1,@last_id=@id,@last_name=@name
    end -- while
*/
-- sp__lock @dbg=1
select @sql=null
select @sql=coalesce(@sql,'''')+''
    update #lock set obj_name=o.name
    from #lock l with (nolock)
    join ''+quotename([db_name])+''..sysobjects o with (nolock)
    on l.objid=o.id''
from #lock l
group by [db_name]
if @dbg=1 and len(@sql)>3990 exec sp__printf ''%s'',@sql
exec(@sql)

exec sp__printf ''-- sp__lock:%T: output'',''%t''  -- ''%t'' otherwise do not print date

-- select count(*) from #lock
if @dbg=1 select [db_name],spid from #lock with (nolock)
-- master..xp_fixeddrives
/*
    show lock in order:
    * more locked objs
    * lock most far (waittime desc or last_batch-current_timestamp)
    * that have more open_trans
    * that have a object name
    * lock of different login name
*/
select @n=count(*) from #lock l with (nolock)
exec sp__printf ''-- sp__lock:%T: filtering %d records by:%s or spid %d'',@n,@obj,@spid

-- declare @timer datetime
select  l.spid,l.[db_name],replace(l.obj_name,''___'',''.'') as obj_name,l.type,l.mode,
        left(l.login,16) as [login],l.host,l.sql,l.p_status,l.resource,l.open_trans,l.status,
        datediff(ss,l.last_batch,@timer) as rsecs,l.blocking,l.n_locks
from #lock l with (nolock)
where (@spid is null or l.spid=@spid)
and (
    (@obj is null or l.obj_name like @obj)
    or (@obj is null or l.login like @obj)
    )
and ([type]!=''DB'' and mode!=''S'') -- shared db locks
and (not l.blocking is null or @blocking=0)
order by n_locks desc,l.spid,[db_name],obj_name -- datediff(ss,l.last_batch,@timer) desc,l.open_trans desc,l.obj_name desc

exec sp__printf ''-- sp__lock:%T: drop temp tables'',''%t''  -- ''%t'' otherwise do not print date

drop table #processes
drop table #lock

end -- sp__lock'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__lock:

-- ================================================================= sp__lock_ex
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__lock_ex',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130203
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__lock_ex') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__lock_ex') with nowait
        goto skip_sp__lock_ex
        end
    if @ver>130203
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__lock_ex') with nowait
        goto skip_sp__lock_ex
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__lock_ex') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__lock_ex'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__lock_ex]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130203\s.zaglio: added cxpackets locks
    v:120911\s.zaglio: added warning on missing help
    v:120905\s.zaglio: added code for finding the SPID of running jobs
    v:111213\s.zaglio: blocking into #temp because out of memory
    v:111129\s.zaglio: added view of blocking in current database
    v:110921\s.zaglio: emaciated
    v:110916\s.zaglio: added list of n locks x db
    v:101110\s.zaglio: removed original_login_name,original_security_id(sp3) to make it compatible with sp2
    v:101012\s.zaglio: not tested but probably better than my sp__lock
    t:exec sp__lock_ex ; exec sp__lock_ex @procdata=''a''
    c:
        originally from http://www.sommarskog.se/sqlutil/beta_lockinfo.sp
        i removed comments and text mode to reduce compile time
*/
CREATE proc sp__lock_ex
    @allprocesses bit     = 0,
    @procdata     char(1) = null,
    @debug        bit     = 0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

if dbo.fn__ismssql2k()=0
    begin

    -- cx packets locks
    select session_id
            , exec_context_id
            , wait_type
            , wait_duration_ms
            , blocking_session_id
    from sys.dm_os_waiting_tasks
    where session_id > 50
    order by session_id, exec_context_id

    -- number of lock records per database
    select count(*) as ''numberoflockrecords'', db_name(resource_database_id)
    from master.sys.dm_tran_locks
    group by resource_database_id;

-- ========================================= finding the SPID of running jobs ==
    -- from http://social.msdn.microsoft.com/Forums/eu/transactsql/thread/8af7ac39-7b09-4b84-9c1c-95573c7350d8
    DECLARE @record_id int, @SQLProcessUtilization int, @CPU int,@EventTime datetime--,@MaxCPUAllowed int
    select  top 1  @record_id =record_id,
          @EventTime=dateadd(ms, -1 * ((SELECT ms_ticks from sys.dm_os_sys_info) - [timestamp]), GetDate()),-- as EventTime,
          @SQLProcessUtilization=SQLProcessUtilization,
          --SystemIdle,
          --100 - SystemIdle - SQLProcessUtilization as OtherProcessUtilization,
          @CPU=SQLProcessUtilization + (100 - SystemIdle - SQLProcessUtilization) --as CPU_Usage
    from (
          select
                record.value(''(./Record/@id)[1]'', ''int'') as record_id,
                record.value(''(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]'', ''int'') as SystemIdle,
                record.value(''(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]'', ''int'') as SQLProcessUtilization,
                timestamp
          from (
                select timestamp, convert(xml, record) as record
                from sys.dm_os_ring_buffers
                where ring_buffer_type = N''RING_BUFFER_SCHEDULER_MONITOR''
                and record like ''%<SystemHealth>%'') as x
          ) as y
    order by record_id desc
    SELECT
            x.session_id as [Sid],
            COALESCE(x.blocking_session_id, 0) as BSid,
            @CPU as CPU,
            @SQLProcessUtilization as SQL,

            x.Status,
            x.TotalCPU as [T.CPU],
            x.Start_time,
            CONVERT(nvarchar(30), getdate()-x.Start_time, 108) as Elap_time, --x.totalElapsedTime as ElapTime,
            x.totalReads as [T.RD], -- total reads
            x.totalWrites as [T.WR], --total writes
            x.Writes_in_tempdb as [W.TDB],
            (
                  SELECT substring(text,x.statement_start_offset/2,
                        (case when x.statement_end_offset = -1
                        then len(convert(nvarchar(max), text)) * 2
                        else x.statement_end_offset end - x.statement_start_offset+3)/2)
                  FROM sys.dm_exec_sql_text(x.sql_handle)
                  FOR XML PATH(''''), TYPE
            ) AS Sql_text,
            db_name(x.database_id) as dbName,
            (SELECT object_name(objectid) FROM sys.dm_exec_sql_text(x.sql_handle)) as object_name,
            x.Wait_type,
            x.Login_name,
            x.Host_name,
            CASE LEFT(x.program_name,15)
            WHEN ''SQLAgent - TSQL'' THEN
            (     select top 1 ''SQL Job = ''+j.name from msdb.dbo.sysjobs (nolock) j
                  inner join msdb.dbo.sysjobsteps (nolock) s on j.job_id=s.job_id
                  where right(cast(s.job_id as nvarchar(50)),10) = RIGHT(substring(x.program_name,30,34),10) )
            WHEN ''SQL Server Prof'' THEN ''SQL Server Profiler''
            ELSE x.program_name
            END as Program_name,
            x.percent_complete,
            x.percent_complete,
            (
                  SELECT
                        p.text
                  FROM
                  (
                        SELECT
                             sql_handle,statement_start_offset,statement_end_offset
                        FROM sys.dm_exec_requests r2
                        WHERE
                             r2.session_id = x.blocking_session_id
                  ) AS r_blocking
                  CROSS APPLY
                  (
                  SELECT substring(text,r_blocking.statement_start_offset/2,
                        (case when r_blocking.statement_end_offset = -1
                        then len(convert(nvarchar(max), text)) * 2
                        else r_blocking.statement_end_offset end - r_blocking.statement_start_offset+3)/2)
                  FROM sys.dm_exec_sql_text(r_blocking.sql_handle)
                  FOR XML PATH(''''), TYPE
                  ) p (text)
            )  as blocking_text,
            (SELECT object_name(objectid) FROM sys.dm_exec_sql_text(
            (select top 1 sql_handle FROM sys.dm_exec_requests r3 WHERE r3.session_id = x.blocking_session_id))) as blocking_obj

      FROM
      (
            SELECT
                  r.session_id,
                  s.host_name,
                  s.login_name,
                  r.start_time,
                  r.sql_handle,
                  r.database_id,
                  r.blocking_session_id,
                  r.wait_type,
                  r.status,
                  r.statement_start_offset,
                  r.statement_end_offset,
                  s.program_name,
                  r.percent_complete,
                  SUM(cast(r.total_elapsed_time as bigint)) /1000 as totalElapsedTime, --CAST AS BIGINT to fix invalid data convertion when high activity
                  SUM(cast(r.reads as bigint)) AS totalReads,
                  SUM(cast(r.writes as bigint)) AS totalWrites,
                  SUM(cast(r.cpu_time as bigint)) AS totalCPU,
                  SUM(tsu.user_objects_alloc_page_count + tsu.internal_objects_alloc_page_count) AS writes_in_tempdb
            FROM sys.dm_exec_requests r
            JOIN sys.dm_exec_sessions s ON s.session_id = r.session_id
            JOIN sys.dm_db_task_space_usage tsu ON s.session_id = tsu.session_id and r.request_id = tsu.request_id
            WHERE r.status IN (''running'', ''runnable'', ''suspended'')
            GROUP BY
                  r.session_id,
                  s.host_name,
                  s.login_name,
                  r.start_time,
                  r.sql_handle,
                  r.database_id,
                  r.blocking_session_id,
                  r.wait_type,
                  r.status,
                  r.statement_start_offset,
                  r.statement_end_offset,
                  s.program_name,
                  r.percent_complete
      ) x
      where x.session_id <> @@spid
      order by x.totalCPU desc
-- ========================================= finding the SPID of running jobs ==

    --View Blocking in Current Database
    --Author: Timothy Ford
    --http://thesqlagentman.com
    --drop table #blocking
    select dtl.resource_type,
       case
           when dtl.resource_type in (''database'', ''file'', ''metadata'') then dtl.resource_type
           when dtl.resource_type = ''object'' then object_name(dtl.resource_associated_entity_id)
           when dtl.resource_type in (''key'', ''page'', ''rid'') then
               (
               select object_name([object_id])
               from sys.partitions
               where sys.partitions.hobt_id =
               dtl.resource_associated_entity_id
               )
           else ''unidentified''
       end as requested_object_name, dtl.request_mode, dtl.request_status,
       dowt.wait_duration_ms, dowt.wait_type, dowt.session_id as [blocked_session_id],
       sp_blocked.[loginame] as [blocked_user],
       convert(nvarchar(max),null) as [blocked_command],
       -- dest_blocked.[text] as [blocked_command],
       dowt.blocking_session_id, sp_blocking.[loginame] as [blocking_user],
       convert(nvarchar(max),null) as [blocking_command],
       -- dest_blocking.[text] as [blocking_command],
       dowt.resource_description,
       sp_blocked.[sql_handle] as sp_blocked_sql_handle,
       sp_blocking.[sql_handle] as sp_blocking_sql_handle
    into #blocking
    -- select *
    from sys.dm_tran_locks as dtl with (nolock)
       inner join sys.dm_os_waiting_tasks as dowt with (nolock)
           on dtl.lock_owner_address = dowt.resource_address
       inner join sys.sysprocesses as sp_blocked with (nolock)
           on dowt.[session_id] = sp_blocked.[spid]
       inner join sys.sysprocesses as sp_blocking with (nolock)
           on dowt.[blocking_session_id] = sp_blocking.[spid]

    update #blocking set
        [blocked_command]=dest_blocked.[text],
        [blocking_command]=dest_blocking.[text]
    from #blocking b
       cross apply sys.[dm_exec_sql_text](sp_blocked_sql_handle) as dest_blocked
       cross apply sys.[dm_exec_sql_text](sp_blocking_sql_handle) as dest_blocking
    -- where dtl.[resource_database_id] = db_id()

    select * from #blocking
    drop table #blocking
    end -- view of n of locks and blocking

-- version check
declare @version  varchar(20)
select  @version = convert(varchar, serverproperty(''productversion''))
if @version not like ''[0-9][0-9].%''
   select @version = ''0'' + @version
if @version < ''09.00.3042''
    begin
    raiserror(''beta_lockinfo requires sql server 2005 sp2 or later'', 16, 127)
    goto ret
    end

-- compatibility level check
if (select compatibility_level
    from   sys.databases
    where  name = db_name()) < 90
    begin
    raiserror(''you cannot install beta_lockinfo in database with compat.level 80 or lower.'', 16, 127)
    goto ret
    end

-- this table holds the information in sys.dm_tran_locks, aggregated
-- on a number of items. note that we do not include subthreads or
-- requests in the aggregation.
declare @locks table (
   session_id      int  not null,
   req_mode        varchar(60)   collate latin1_general_bin2 not null,
   rsc_type        varchar(60)   collate latin1_general_bin2 not null,
   rsc_subtype     varchar(60)   collate latin1_general_bin2 not null,
   req_status      varchar(60)   collate latin1_general_bin2 not null,
   req_owner_type  varchar(60)   collate latin1_general_bin2 not null,
   rsc_description nvarchar(256) collate latin1_general_bin2 null,
   database_id     int      not null,
   entity_id       bigint   null,
   cnt             int      not null,
   activelock as case when rsc_type = ''database'' and
                           req_status = ''grant''
                      then convert(bit, 0)
                      else convert(bit, 1)
                 end,
   ident          int identity primary key,
   rowno          int null     -- set per session_id if @procdata is f.
)

-- this table holds the translation of entity_id in @locks. this is a
-- temp table since we access it from dynamic sql. the type_desc is used
-- for allocation units.
create table #objects (
     idtype         char(4)       not null
        check (idtype in (''obj'', ''hobt'', ''au'', ''misc'')),
     database_id    int           not null,
     entity_id      bigint        not null,
     hobt_id        bigint        null,
     object_name    nvarchar(550) collate latin1_general_bin2 null,
     type_desc      varchar(60)   collate latin1_general_bin2 null,
     primary key clustered (database_id, idtype, entity_id),
     unique nonclustered (database_id, entity_id, idtype)
)

-- this table captures sys.dm_os_waiting_tasks and later augment it with
-- data about the block chain. a waiting task always has a always has a
-- task address, but the blocker may be idle and without a task.
-- all columns for the blocker are nullable, as we add extra rows for
-- non-waiting blockers.
declare @dm_os_waiting_tasks table
   (wait_session_id   smallint     not null,
    wait_task         varbinary(8) not null,
    block_session_id  smallint     null,
    block_task        varbinary(8) null,
    wait_type         varchar(60) collate latin1_general_bin2  null,
    wait_duration_ms  bigint       null,
    -- the level in the chain. level 0 is the lead blocker. null for
    -- tasks that are waiting, but not blocking.
    block_level       smallint     null,
    -- the lead blocker for this block chain.
    lead_blocker_spid smallint     null,
    -- whether the block chain consists of the threads of the same spid only.
    blocksamespidonly bit         not null default 0,
  unique clustered (wait_session_id, wait_task, block_session_id, block_task),
  unique (block_session_id, block_task, wait_session_id, wait_task)
)

-- this table holds information about transactions tied to a session.
-- a session can have multiple transactions when there are multiple
-- requests, but in that case we only save data about the oldest
-- transaction.
declare @transactions table (
   session_id       smallint      not null,
   is_user_trans    bit           not null,
   trans_start      datetime      not null,
   trans_since      decimal(10,3) null,
   trans_type       int           not null,
   trans_state      int           not null,
   dtc_state        int           not null,
   is_bound         bit           not null,
   primary key (session_id)
)


-- this table holds information about all sessions and requests.
declare @procs table (
   session_id       smallint      not null,
   task_address     varbinary(8)  not null,
   exec_context_id  int           not null,
   request_id       int           not null,
   spidstr as ltrim(str(session_id)) +
              case when exec_context_id <> 0 or request_id <> 0
                   then ''/'' + ltrim(str(exec_context_id)) +
                        ''/'' + ltrim(str(request_id))
                   else ''''
              end,
   is_user_process  bit           not null,
   orig_login       nvarchar(128) collate latin1_general_bin2 null,
   current_login    nvarchar(128) collate latin1_general_bin2 null,
   session_state    varchar(30)   collate latin1_general_bin2 not null,
   task_state       varchar(60)   collate latin1_general_bin2 null,
   proc_dbid        smallint      null,
   request_dbid     smallint      null,
   host_name        nvarchar(128) collate latin1_general_bin2 null,
   host_process_id  int           null,
   endpoint_id      int           not null,
   program_name     nvarchar(128) collate latin1_general_bin2 null,
   request_command  varchar(32)   collate latin1_general_bin2 null,
   trancount        int           not null,
   session_cpu      int           not null,
   request_cpu      int           null,
   session_physio   bigint        not null,
   request_physio   bigint        null,
   session_logreads bigint        not null,
   request_logreads bigint        null,
   isclr            bit           not null default 0,
   nest_level       int           null,
   now              datetime      not null,
   login_time       datetime      not null,
   last_batch       datetime      not null,
   last_since       decimal(10,3) null,
   curdbid          smallint      null,
   curobjid         int           null,
   current_stmt     nvarchar(max) collate latin1_general_bin2 null,
   sql_handle       varbinary(64) null,
   plan_handle      varbinary(64) null,
   stmt_start       int           null,
   stmt_end         int           null,
   current_plan     xml           null,
   rowno            int           not null,
   block_level      tinyint       null,
   block_session_id smallint      null,
   block_exec_context_id int      null,
   block_request_id      int      null,
   blockercnt        int          null,
   block_spidstr as ltrim(str(block_session_id)) +
               case when block_exec_context_id <> 0 or block_request_id <> 0
                    then ''/'' + ltrim(str(block_exec_context_id)) +
                         ''/'' + ltrim(str(block_request_id))
                    else ''''
               end +
               case when blockercnt > 1
                    then '' (+'' + ltrim(str(blockercnt - 1)) + '')''
                    else ''''
               end,
   blocksamespidonly bit          not null default 0,
   waiter_no_blocker bit          not null default 0,
   wait_type        varchar(60)   collate latin1_general_bin2 null,
   wait_time        decimal(18,3) null,
   primary key (session_id, task_address))


-- output from dbcc inputbuffer. the identity column is there to make
-- it possible to add the spid later.
declare @inputbuffer table
       (eventtype    nvarchar(30)   null,
        params       int            null,
        inputbuffer  nvarchar(4000) null,
        ident        int            identity unique,
        spid         int            not null default 0 primary key)

------------------------------------------------------------------------
-- local variables.
------------------------------------------------------------------------
declare @now             datetime,
        @ms              int,
        @spid            smallint,
        @rowc            int,
        @lvl             int,
        @dbname          sysname,
        @dbidstr         varchar(10),
        @stmt            nvarchar(max),
        @request_id      int,
        @handle          varbinary(64),
        @stmt_start      int,
        @stmt_end        int;

------------------------------------------------------------------------
-- set up.
------------------------------------------------------------------------
-- all reads are dirty! the most important reason for this is tempdb.sys.objects.
set transaction isolation level read uncommitted;
set nocount on;

-- processes below @minspid are system processes.
select @now = getdate();

-- validate the @procdata parameter, and set default.
if @procdata is null
   select @procdata = ''f''

if @procdata not in (''a'', ''f'')
begin
   raiserror(''invalid value for @procdata parameter. a and f are permitted'', 16, 1)
   return
end

------------------------------------------------------------------------
-- first capture all locks. we aggregate by type, object etc to keep
-- down the volume.
------------------------------------------------------------------------
if @debug = 1
begin
   raiserror (''compiling lock information, time 0 ms.'', 0, 1) with nowait
end;

-- we force binary collation, to make the group by operation faster.
with cte as (
   select request_session_id,
          req_mode        = request_mode       collate latin1_general_bin2,
          rsc_type        = resource_type      collate latin1_general_bin2,
          rsc_subtype     = resource_subtype   collate latin1_general_bin2,
          req_status      = request_status     collate latin1_general_bin2,
          req_owner_type  = request_owner_type collate latin1_general_bin2,
          rsc_description =
             case when resource_type = ''application''
                  then nullif(resource_description
                              collate latin1_general_bin2, '''')
             end,
          resource_database_id, resource_associated_entity_id
    from  sys.dm_tran_locks)
insert @locks (session_id, req_mode, rsc_type, rsc_subtype, req_status,
               req_owner_type, rsc_description,
               database_id, entity_id, cnt)
   select request_session_id, req_mode, rsc_type, rsc_subtype, req_status,
          req_owner_type, rsc_description,
          resource_database_id, resource_associated_entity_id,
          count(*)
   from   cte
   group  by request_session_id, req_mode, rsc_type, rsc_subtype, req_status,
          req_owner_type, rsc_description,
          resource_database_id, resource_associated_entity_id

-----------------------------------------------------------------------
-- get the blocking chain.
-----------------------------------------------------------------------
if @debug = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''determining blocking chain, time %d ms.'', 0, 1, @ms) with nowait
end

-- first capture sys.dm_os_waiting_tasks, skipping non-spid tasks. the
-- distinct is needed, because there may be duplicates. (i''ve seen them.)
insert @dm_os_waiting_tasks (wait_session_id, wait_task, block_session_id,
                             block_task, wait_type, wait_duration_ms)
   select distinct
          owt.session_id, owt.waiting_task_address, owt.blocking_session_id,
          case when owt.blocking_session_id is not null
               then coalesce(owt.blocking_task_address, 0x)
          end, owt.wait_type, owt.wait_duration_ms
   from   sys.dm_os_waiting_tasks owt
   where  owt.session_id is not null;

-----------------------------------------------------------------------
-- get transaction.
-----------------------------------------------------------------------
if @debug = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''determining active transactions, time %d ms.'', 0, 1, @ms) with nowait
end

; with oldest_tran as (
    select tst.session_id, tst.is_user_transaction,
           tat.transaction_begin_time, tat.transaction_type,
           tat.transaction_state, tat.dtc_state, tst.is_bound,
           rowno = row_number() over (partition by tst.session_id
                                      order by tat.transaction_begin_time asc)
    from   sys.dm_tran_session_transactions tst
    join   sys.dm_tran_active_transactions tat
       on  tst.transaction_id = tat.transaction_id
)
insert @transactions(session_id, is_user_trans, trans_start,
                     trans_since,
                     trans_type, trans_state, dtc_state, is_bound)
   select session_id, is_user_transaction, transaction_begin_time,
          case when datediff(day, transaction_begin_time, @now) > 20
               then null
               else datediff(ms, transaction_begin_time,  @now) / 1000.000
          end,
          transaction_type, transaction_state, dtc_state, is_bound
   from   oldest_tran
   where  rowno = 1

------------------------------------------------------------------------
-- then get the processes. we filter here for active processes once for all
------------------------------------------------------------------------
if @debug = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''collecting process information, time %d ms.'', 0, 1, @ms) with nowait
end

insert @procs(session_id, task_address,
              exec_context_id, request_id,
              is_user_process,
              current_login,
              orig_login,
              session_state, task_state, endpoint_id, proc_dbid, request_dbid,
              host_name, host_process_id, program_name, request_command,
              trancount,
              session_cpu, request_cpu,
              session_physio, request_physio,
              session_logreads, request_logreads,
              isclr, nest_level,
              now, login_time, last_batch,
              last_since,
              sql_handle, plan_handle,
              stmt_start, stmt_end,
              rowno)
   select es.session_id, coalesce(ot.task_address, 0x),
          coalesce(ot.exec_context_id, 0), coalesce(er.request_id, 0),
          es.is_user_process,
          coalesce(nullif(es.login_name, ''''), suser_sname(es.security_id)),
          coalesce(nullif(es.login_name, ''''), -- ex original_login_name
                   suser_sname(es.security_id)), -- ex original_security_id
          es.status, ot.task_state, es.endpoint_id, sp.dbid, er.database_id,
          es.host_name, es.host_process_id, es.program_name, er.command,
          coalesce(er.open_transaction_count, sp.open_tran),
          es.cpu_time, er.cpu_time,
          es.reads + es.writes, er.reads + er.writes,
          es.logical_reads, er.logical_reads,
          coalesce(er.executing_managed_code, 0), er.nest_level,
          @now, es.login_time, es.last_request_start_time,
          case when datediff(day, es.last_request_start_time, @now) > 20
               then null
               else datediff(ms, es.last_request_start_time,  @now) / 1000.000
          end,
          er.sql_handle, er.plan_handle,
          er.statement_start_offset, er.statement_end_offset,
          rowno = row_number() over (partition by es.session_id
                                     order by ot.exec_context_id, er.request_id)
   -- select *
   from   sys.dm_exec_sessions es
   join   (select spid, dbid = min(dbid), open_tran = min(open_tran)
           from   sys.sysprocesses
           where  ecid = 0
           group  by spid) as sp on sp.spid = es.session_id
   left   join sys.dm_os_tasks ot on es.session_id = ot.session_id
   left   join sys.dm_exec_requests er on ot.task_address = er.task_address
   where  -- all processes requested
          @allprocesses > 0
          -- all user sessions with a running request save ourselevs.
      or  ot.exec_context_id is not null and
          es.is_user_process = 1  and
          es.session_id <> @@spid
          -- all sessions with an open transaction, even if they are idle.
     or   sp.open_tran > 0 and es.session_id <> @@spid
          -- all sessions that have an interesting lock, save ourselves.
     or   exists (select *
                   from   @locks l
                   where  l.session_id = es.session_id
                     and  l.activelock = 1) and es.session_id <> @@spid
          -- all sessions that is blocking someone.
     or   exists (select *
                  from   @dm_os_waiting_tasks owt
                  where  owt.block_session_id = es.session_id)

------------------------------------------------------------------------
-- get input buffers. note that we can only find one per session, even
-- a session has several requests.
-- we skip this part if @@nestlevel is > 1, as presumably we are calling
-- ourselves recursively from insert exec, and we may no not do another
-- level of insert-exec.
------------------------------------------------------------------------
if @@nestlevel = 1
begin
   if @debug = 1
   begin
      select @ms = datediff(ms, @now, getdate())
      raiserror (''getting input buffers, time %d ms.'', 0, 1, @ms) with nowait
   end

   declare c1 cursor fast_forward local for
      select distinct session_id
      from   @procs
      where  is_user_process = 1
   open c1

   while 1 = 1
   begin
      fetch c1 into @spid
      if @@fetch_status <> 0
         break

      begin try
         insert @inputbuffer(eventtype, params, inputbuffer)
            exec sp_executesql N''dbcc inputbuffer (@spid) with no_infomsgs'',
                               N''@spid int'', @spid

         update @inputbuffer
         set    spid = @spid
         where  ident = scope_identity()
      end try
      begin catch
         insert @inputbuffer(inputbuffer, spid)
            values(''error getting inputbuffer: '' + error_message(), @spid)
      end catch
  end

   deallocate c1
end

-----------------------------------------------------------------------
-- compute the blocking chain.
-----------------------------------------------------------------------
if @debug = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''computing blocking chain, time %d ms.'', 0, 1, @ms) with nowait
end

-- mark blockers that are waiting, that is waiting for something else
-- than another spid.
update @dm_os_waiting_tasks
set    block_level = 0,
       lead_blocker_spid = a.wait_session_id
from   @dm_os_waiting_tasks a
where  a.block_session_id is null
  and  exists (select *
               from   @dm_os_waiting_tasks b
               where  a.wait_session_id = b.block_session_id
                 and  a.wait_task       = b.block_task)
select @rowc = @@rowcount

-- add an extra row for blockers that are not waiting at all.
insert @dm_os_waiting_tasks (wait_session_id, wait_task,
                             block_level, lead_blocker_spid)
   select distinct a.block_session_id, coalesce(a.block_task, 0x),
                   0, a.block_session_id
   from   @dm_os_waiting_tasks a
   where  not exists (select *
                      from  @dm_os_waiting_tasks b
                      where a.block_session_id = b.wait_session_id
                        and a.block_task       = b.wait_task)
     and  a.block_session_id is not null;

select @rowc = @rowc + @@rowcount, @lvl = 0

-- then iterate as long as we find blocked processes. you may think
-- that a recursive cte would be great here, but we want to exclude
-- rows that has already been marked. this is difficult to do with a cte.
while @rowc > 0
begin
   update a
   set    block_level = b.block_level + 1,
          lead_blocker_spid = b.lead_blocker_spid
   from   @dm_os_waiting_tasks a
   join   @dm_os_waiting_tasks b on a.block_session_id = b.wait_session_id
                                and a.block_task       = b.wait_task
   where  b.block_level = @lvl
     and  a.block_level is null

  select @rowc = @@rowcount, @lvl = @lvl + 1
end

-- next to find are processes that are blocked, but no one is waiting for.
-- they are directly or indirectly blocked by a deadlock. they get a
-- negative level initially. we clean this up later.
update @dm_os_waiting_tasks
set    block_level = -1
from   @dm_os_waiting_tasks a
where  a.block_level is null
  and  a.block_session_id is not null
  and  not exists (select *
                   from   @dm_os_waiting_tasks b
                   where  b.block_session_id = a.wait_session_id
                     and  b.block_task       = a.wait_task)

select @rowc = @@rowcount, @lvl = -2

-- then unwind these chains in the opposite direction to before.
while @rowc > 0
begin
   update @dm_os_waiting_tasks
   set    block_level = @lvl
   from   @dm_os_waiting_tasks a
   where  a.block_level is null
     and  a.block_session_id is not null
     and  not exists (select *
                      from   @dm_os_waiting_tasks b
                      where  b.block_session_id = a.wait_session_id
                        and  b.block_task       = a.wait_task
                        and  b.block_level is null)
   select @rowc = @@rowcount, @lvl = @lvl - 1
end

-- determine which blocking tasks that only block tasks within the same
-- spid.
update @dm_os_waiting_tasks
set    blocksamespidonly = 1
from   @dm_os_waiting_tasks a
where  a.block_level is not null
  and  a.wait_session_id = a.lead_blocker_spid
  and  not exists (select *
                   from   @dm_os_waiting_tasks b
                   where  a.wait_session_id = b.lead_blocker_spid
                     and  a.wait_session_id <> b.wait_session_id)

-----------------------------------------------------------------------
-- add block-chain and wait information to @procs. if a blockee has more
-- than one blocker, we pick one.
-----------------------------------------------------------------------
if @debug = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''adding blocking chain to @procs, time %d ms.'', 0, 1, @ms) with nowait
end

; with block_chain as (
    select wait_session_id, wait_task, block_session_id, block_task,
           block_level = case when block_level >= 0 then block_level
                              else block_level - @lvl - 1
                         end,
    wait_duration_ms, wait_type, blocksamespidonly,
    cnt   = count(*) over (partition by wait_task),
    rowno = row_number() over (partition by wait_task
                               order by block_level, block_task)
    from @dm_os_waiting_tasks
)
update p
set    block_level           = bc.block_level,
       block_session_id      = bc.block_session_id,
       block_exec_context_id = coalesce(p2.exec_context_id, -1),
       block_request_id      = coalesce(p2.request_id, -1),
       blockercnt            = bc.cnt,
       blocksamespidonly     = bc.blocksamespidonly,
       wait_time             = convert(decimal(18, 3), bc.wait_duration_ms) / 1000,
       wait_type             = bc.wait_type
from   @procs p
join   block_chain bc on p.session_id   = bc.wait_session_id
                     and p.task_address = bc.wait_task
                     and bc.rowno = 1
left   join @procs p2 on bc.block_session_id = p2.session_id
                     and bc.block_task       = p2.task_address

--------------------------------------------------------------------
-- delete "uninteresting" locks from @locks for processes not in @procs.
--------------------------------------------------------------------
if @allprocesses = 0
begin
   if @debug = 1
   begin
      select @ms = datediff(ms, @now, getdate())
      raiserror (''deleting uninteresting locks, time %d ms.'', 0, 1, @ms) with nowait
   end

   delete @locks
   from   @locks l
   where  (activelock = 0 or session_id = @@spid)
     and  not exists (select *
                      from   @procs p
                      where  p.session_id = l.session_id)
end

-----------------------------------------------------------------------
-- get object names from ids in @procs and @locks. you may think that
-- we could use object_name and its second database parameter, but
-- object_name takes out a sch-s lock (even with read uncommitted) and
-- gets blocked if a object (read temp table) has been created in a transaction.
-----------------------------------------------------------------------
if @debug = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''getting object names, time %d ms.'', 0, 1, @ms) with nowait
end

-- first get all entity ids into the temp table. we can translate
-- object ids now. and we save the database name as a fallback for
-- those where do not translate more. yes, we save the entity id twice.
insert #objects (idtype, database_id, entity_id, hobt_id)
   select distinct
          case when rsc_type = ''object'' then ''obj''
               when rsc_type in (''page'', ''key'', ''rid'', ''hobt'') then ''hobt''
               when rsc_type = ''allocation_unit'' then ''au''
               else ''misc''
          end,
          database_id, entity_id, entity_id
   from   @locks
   where  rsc_type in (''page'', ''key'', ''rid'', ''hobt'', ''allocation_unit'',
                       ''object'')
   union
   select distinct ''obj'', curdbid, curobjid, curobjid
   from   @procs
   where  curdbid is not null
     and  curobjid is not null


declare c2 cursor static local for
   select distinct str(database_id),
                   quotename(db_name(database_id))
   from   #objects
   where  idtype in  (''obj'', ''hobt'', ''au'')
   option (keepfixed plan)

open c2

while 1 = 1
begin
   fetch c2 into @dbidstr, @dbname
   if @@fetch_status <> 0
      break

   -- first handle allocation units. they bring us a hobt_id, or we go
   -- directly to the object when the container is a partition_id. we
   -- always get the type_desc. to make the dynamic sql easier to read,
   -- we use some placeholders.
   select @stmt = ''
      update #objects
      set    type_desc = au.type_desc,
             hobt_id   = case when au.type in (1, 3)
                              then au.container_id
                         end,
             idtype    = case when au.type in (1, 3)
                              then "hobt"
                              else "au"
                         end,
             object_name = case when au.type = 2 then
                              db_name(@dbidstr) + "." +
                              s.name + "." + o.name +
                              case when p.index_id <= 1
                                   then ""
                                   else "." + i.name
                              end +
                              case when p.partition_number > 1
                                   then "(" +
                                         ltrim(str(p.partition_number)) +
                                        ")"
                                   else ""
                              end
                              when au.type = 0 then
                                 db_name(@dbidstr) + " (dropped table et al)"
                           end
      from   #objects ob
      join   @dbname.sys.allocation_units au on ob.entity_id = au.allocation_unit_id
      -- we should only go all the way from sys.partitions, for type = 3.
      left   join  (@dbname.sys.partitions p
                    join    @dbname.sys.objects o on p.object_id = o.object_id
                    join    @dbname.sys.indexes i on p.object_id = i.object_id
                                                 and p.index_id  = i.index_id
                    join    @dbname.sys.schemas s on o.schema_id = s.schema_id)
         on  au.container_id = p.partition_id
        and  au.type = 2
      where  ob.database_id = @dbidstr
        and  ob.idtype = "au"
      option (keepfixed plan);
   ''

   -- now we can translate all hobt_id, including those we got from the
   -- allocation units.
   select @stmt = @stmt + ''
      update #objects
      set    object_name = db_name(@dbidstr) + "." + s.name + "." + o.name +
                           case when p.index_id <= 1
                                then ""
                                else "." + i.name
                           end +
                           case when p.partition_number > 1
                                then "(" +
                                      ltrim(str(p.partition_number)) +
                                     ")"
                                else ""
                           end + coalesce(" (" + ob.type_desc + ")", "")
      from   #objects ob
      join   @dbname.sys.partitions p on ob.hobt_id  = p.hobt_id
      join   @dbname.sys.objects o    on p.object_id = o.object_id
      join   @dbname.sys.indexes i    on p.object_id = i.object_id
                                     and p.index_id  = i.index_id
      join   @dbname.sys.schemas s    on o.schema_id = s.schema_id
      where  ob.database_id = @dbidstr
        and  ob.idtype = "hobt"
      option (keepfixed plan)
      ''

   -- and now object ids, idtype = obj.
   select @stmt = @stmt + ''
      update #objects
      set    object_name = db_name(@dbidstr) + "." +
                           coalesce(s.name + "." + o.name,
                                    "<" + ltrim(str(ob.entity_id)) + ">")
      from   #objects ob
      left   join   (@dbname.sys.objects o
                     join @dbname.sys.schemas s on o.schema_id = s.schema_id)
             on convert(int, ob.entity_id) = o.object_id
      where  ob.database_id = @dbidstr
        and  ob.idtype = "obj"
      option (keepfixed plan)
   ''

   -- fix the placeholders.
   select @stmt = replace(replace(replace(@stmt,
                         ''"'', ''''''''),
                         ''@dbname'', @dbname),
                         ''@dbidstr'', @dbidstr)

   --  and run the beast.
   --print @stmt
   exec (@stmt)
end
deallocate c2

----------------------------------------------------------------------
-- get the query text. this is not done in the main query, as we could
-- be blocked if someone is creating an sp and executes it in a
-- transaction.
----------------------------------------------------------------------
if @@nestlevel = 1
begin
   if @debug = 1
   begin
      select @ms = datediff(ms, @now, getdate())
      raiserror (''retrieving current statement, time %d ms.'', 0, 1, @ms) with nowait
   end

   -- set lock timeout to avoid being blocked.
   set lock_timeout 5

   -- first try to get all query plans in one go.
   begin try
      update @procs
      set    curdbid      = est.dbid,
             curobjid     = est.objectid,
             current_stmt =
             case when est.encrypted = 1
                  then ''-- encrypted, pos '' +
                       ltrim(str((p.stmt_start + 2)/2)) + '' - '' +
                       ltrim(str((p.stmt_end + 2)/2))
                  when p.stmt_start >= 0
                  then substring(est.text, (p.stmt_start + 2)/2,
                                 case p.stmt_end
                                      when -1 then datalength(est.text)
                                    else (p.stmt_end - p.stmt_start + 2) / 2
                                 end)
             end
      from   @procs p
      cross  apply sys.dm_exec_sql_text(p.sql_handle) est
   end try
   begin catch
      -- if this fails, try to get the texts one by one.
      declare text_cur cursor static local for
         select distinct session_id, request_id, sql_handle,
                         stmt_start, stmt_end
         from   @procs
         where  sql_handle is not null
      open text_cur

      while 1 = 1
      begin
         fetch text_cur into @spid, @request_id, @handle,
                             @stmt_start, @stmt_end
         if @@fetch_status <> 0
            break

         begin try
            update @procs
            set    curdbid      = est.dbid,
                   curobjid     = est.objectid,
                   current_stmt =
                   case when est.encrypted = 1
                        then ''-- encrypted, pos '' +
                             ltrim(str((p.stmt_start + 2)/2)) + '' - '' +
                             ltrim(str((p.stmt_end + 2)/2))
                        when p.stmt_start >= 0
                        then substring(est.text, (p.stmt_start + 2)/2,
                                       case p.stmt_end
                                            when -1 then datalength(est.text)
                                          else (p.stmt_end - p.stmt_start + 2) / 2
                                       end)
                   end
            from   @procs p
            cross  apply sys.dm_exec_sql_text(p.sql_handle) est
            where  p.session_id = @spid
              and  p.request_id = @request_id
         end try
         begin catch
             update @procs
             set    current_stmt = ''error: *** '' + error_message() + '' ***''
             where  session_id = @spid
               and  request_id = @request_id
         end catch
      end

      deallocate text_cur

      end catch

   set lock_timeout 0
end


--------------------------------------------------------------------
-- get query plans. the difficult part is that the convert to xml may
-- fail if the plan is too deep. therefore we catch this error, and
-- resort to a cursor in this case. since query plans are not included
-- in text mode, we skip if @nestlevel is > 1.
--------------------------------------------------------------------
if @@nestlevel = 1
begin
   if @debug = 1
   begin
      select @ms = datediff(ms, @now, getdate())
      raiserror (''retrieving query plans, time %d ms.'', 0, 1, @ms) with nowait
   end

   -- adam says that getting the query plans can time out too...
   set lock_timeout 5

   begin try
      update @procs
      set    current_plan = convert(xml, etqp.query_plan)
      from   @procs p
      outer  apply sys.dm_exec_text_query_plan(
                   p.plan_handle, p.stmt_start, p.stmt_end) etqp
      where  p.plan_handle is not null
   end try
   begin catch
      declare plan_cur cursor static local for
         select distinct session_id, request_id, plan_handle,
                         stmt_start, stmt_end
         from   @procs
         where  plan_handle is not null
      open plan_cur

      while 1 = 1
      begin
         fetch plan_cur into @spid, @request_id, @handle,
                             @stmt_start, @stmt_end
         if @@fetch_status <> 0
            break

         begin try
            update @procs
            set    current_plan = (select convert(xml, etqp.query_plan)
                                   from   sys.dm_exec_text_query_plan(
                                      @handle, @stmt_start, @stmt_end) etqp)
            from   @procs p
            where  p.session_id = @spid
              and  p.request_id = @request_id
         end try
         begin catch
            update @procs
            set    current_plan =
                     (select ''could not get query plan'' as [@alert],
                             error_number() as [@errno],
                             error_severity() as [@level],
                             error_message() as [@errmsg]
                      for    xml path(''error''))
            where  session_id = @spid
              and  request_id = @request_id
         end catch
      end

      deallocate plan_cur
   end catch

   set lock_timeout 0

   -- there is a bug in dm_exec_text_query_plan which causes the attribute
   -- statementtext to include the full text of the batch up to current
   -- statement. this causes bloat in ssms. whence we fix the attribute.
   ; with xmlnamespaces(
      ''http://schemas.microsoft.com/sqlserver/2004/07/showplan'' as sp)
   update @procs
   set    current_plan.modify(''
            replace value of (
                  /sp:showplanxml/sp:batchsequence/sp:batch/
                   sp:statements/sp:stmtsimple/@statementtext)[1]
            with
               substring((/sp:showplanxml/sp:batchsequence/sp:batch/
                         sp:statements/sp:stmtsimple/@statementtext)[1],
                        (sql:column("stmt_start") + 2) div 2)
          '')
   where  current_plan is not null
     and  stmt_start is not null
end

--------------------------------------------------------------------
-- if user has selected to see process data only on the first row,
-- we should number the rows in @locks.
--------------------------------------------------------------------
if @procdata = ''f''
begin
   if @debug = 1
   begin
      select @ms = datediff(ms, @now, getdate())
      raiserror (''determining first row, time %d ms.'', 0, 1, @ms) with nowait
   end

   update @locks
   set    rowno = b.rowno
   from   @locks a
   join   (select l.ident,
                  rowno = row_number() over(partition by l.session_id
                    order by case l.req_status
                                  when ''grant'' then ''zzzz''
                                  else l.req_status
                             end, o.object_name, l.rsc_type, l.rsc_description)
           from   @locks l
           left   join   #objects o on l.database_id = o.database_id
                                   and l.entity_id   = o.entity_id) as b
          on a.ident = b.ident
  option (keepfixed plan)
end

---------------------------------------------------------------------
-- before we can join in the locks, we need to make sure that all
-- processes with a running request has a row with exec_context_id =
-- request_id = 0. (those without already has such a row.)
---------------------------------------------------------------------
if @debug = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''supplementing @procs, time %d ms.'', 0, 1, @ms) with nowait
end

insert @procs(session_id, task_address, exec_context_id, request_id,
              is_user_process, orig_login, current_login,
              session_state, endpoint_id, trancount, proc_dbid,
              host_name, host_process_id, program_name,
              session_cpu, session_physio, session_logreads,
              now, login_time, last_batch, last_since, rowno)
   select session_id, 0x, 0, 0,
          is_user_process, orig_login, current_login,
          session_state, endpoint_id, 0, proc_dbid,
          host_name, host_process_id, program_name,
          session_cpu, session_physio, session_logreads,
          now, login_time, last_batch, last_since, 0
    from  @procs a
    where a.rowno = 1
      and not exists (select *
                      from   @procs b
                      where  b.session_id      = a.session_id
                        and  b.exec_context_id = 0
                        and  b.request_id      = 0)

-- a process may be waiting for a lock according sys.dm_os_tran_locks,
-- but it was not in sys.dm_os_waiting_tasks. let''s mark this up.
update @procs
set    waiter_no_blocker = 1
from   @procs p
where  exists (select *
               from   @locks l
               where  l.req_status = ''wait''
                 and  l.session_id = p.session_id
                 and  not exists (select *
                                  from   @procs p2
                                  where  p.session_id = l.session_id))

------------------------------------------------------------------------
-- for plain results we are ready to return now.
------------------------------------------------------------------------
if @debug = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''returning result set, time %d ms.'', 0, 1, @ms) with nowait
end

-- note that the query is a full join, since @locks and @procs may not
-- be in sync. processes may have gone away, or be active without any
-- locks. as for the transactions, we team up with the processes.
select
      readed_at   = getdate(),
      spid        = coalesce(p.spidstr, ltrim(str(l.session_id))),
      command     = case when coalesce(p.exec_context_id, 0) = 0 and
                              coalesce(l.rowno, 1) = 1
                         then p.request_command
                         else ''''
                    end,
      login       = case when coalesce(p.exec_context_id, 0) = 0 and
                              coalesce(l.rowno, 1) = 1
                         then
                         case when p.is_user_process = 0
                              then ''system process''
                              else p.orig_login +
                                 case when p.current_login <> p.orig_login or
                                           p.orig_login is null
                                      then '' ('' + p.current_login + '')''
                                      else ''''
                                 end
                        end
                        else ''''
                    end,
      host        = case when coalesce(p.exec_context_id, 0)= 0 and
                              coalesce(l.rowno, 1) = 1
                         then p.host_name
                         else ''''
                    end,
      appl        = case when coalesce(p.exec_context_id, 0) = 0 and
                              coalesce(l.rowno, 1) = 1
                         then p.program_name
                         else ''''
                    end,
      dbname      = case when coalesce(l.rowno, 1) = 1 and
                              coalesce(p.exec_context_id, 0) = 0
                         then coalesce(db_name(p.request_dbid),
                                       db_name(p.proc_dbid))
                         else ''''
                    end,
      prcstatus   = case when coalesce(l.rowno, 1) = 1
                         then coalesce(p.task_state, p.session_state)
                         else ''''
                    end,
      spid_       = p.spidstr,
      opntrn      = case when p.exec_context_id = 0
                         then coalesce(ltrim(str(nullif(p.trancount, 0))), '''')
                         else ''''
                    end,
      trninfo     = case when coalesce(l.rowno, 1) = 1 and
                              p.exec_context_id = 0 and
                              t.is_user_trans is not null
                         then case t.is_user_trans
                                   when 1 then ''u''
                                   else ''s''
                              end + ''-'' +
                              case t.trans_type
                                   when 1 then ''rw''
                                   when 2 then ''r''
                                   when 3 then ''sys''
                                   when 4 then ''dist''
                                   else ltrim(str(t.trans_type))
                              end + ''-'' +
                              ltrim(str(t.trans_state)) +
                              case t.dtc_state
                                   when 0 then ''''
                                   else ''-''
                              end +
                              case t.dtc_state
                                 when 0 then ''''
                                 when 1 then ''dtc:active''
                                 when 2 then ''dtc:prepared''
                                 when 3 then ''dtc:commited''
                                 when 4 then ''dtc:aborted''
                                 when 5 then ''dtc:recovered''
                                 else ''dtc:'' + ltrim(str(t.dtc_state))
                             end +
                             case t.is_bound
                                when 0 then ''''
                                when 1 then ''-bnd''
                             end
                        else ''''
                    end,
      blklvl      = case when p.block_level is not null
                         then case p.blocksamespidonly
                                   when 1 then ''(''
                                   else ''''
                              end +
                              case when p.block_level = 0
                                   then ''!!''
                                   else ltrim(str(p.block_level))
                              end +
                              case p.blocksamespidonly
                                   when 1 then '')''
                                   else ''''
                              end
                         -- if the process is blocked, but we do not
                         -- have a block level, the process is in a
                         -- dead lock.
                         when p.block_session_id is not null
                         then ''dd''
                         when p.waiter_no_blocker = 1
                         then ''??''
                         else ''''
                    end,
      blkby       = coalesce(p.block_spidstr, ''''),
      cnt         = case when p.exec_context_id = 0 and
                              p.request_id = 0
                         then coalesce(ltrim(str(l.cnt)), ''0'')
                         else ''''
                    end,
      object      = case l.rsc_type
                       when ''application''
                       then coalesce(db_name(l.database_id) + ''|'', '''') +
                                     l.rsc_description
                       else coalesce(o2.object_name,
                                     db_name(l.database_id), '''')
                    end,
      rsctype     = coalesce(l.rsc_type, ''''),
      locktype    = coalesce(l.req_mode, ''''),
      lstatus     = case l.req_status
                         when ''grant'' then lower(l.req_status)
                         else coalesce(l.req_status, '''')
                    end,
      ownertype   = case l.req_owner_type
                         when ''shared_transaction_workspace'' then ''stw''
                         else coalesce(l.req_owner_type, '''')
                    end,
      rscsubtype  = coalesce(l.rsc_subtype, ''''),
      waittime    = case when coalesce(l.rowno, 1) = 1
                         then coalesce(ltrim(str(p.wait_time, 18, 3)), '''')
                         else ''''
                    end,
      waittype    = case when coalesce(l.rowno, 1) = 1
                         then coalesce(p.wait_type, '''')
                         else ''''
                    end,
      spid__      = p.spidstr,
      cpu         = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then coalesce(ltrim(str(p.session_cpu)), '''') +
                         case when p.request_cpu is not null
                              then '' ('' + ltrim(str(p.request_cpu)) + '')''
                              else ''''
                         end
                         else ''''
                    end,
      physio      = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then coalesce(ltrim(str(p.session_physio, 18)), '''') +
                         case when p.request_physio is not null
                              then '' ('' + ltrim(str(p.request_physio)) + '')''
                              else ''''
                         end
                         else ''''
                    end,
      logreads    = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then coalesce(ltrim(str(p.session_logreads, 18)), '''')  +
                         case when p.request_logreads is not null
                              then '' ('' + ltrim(str(p.request_logreads)) + '')''
                              else ''''
                         end
                         else ''''
                    end,
      now         = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then convert(char(12), p.now, 114)
                         else ''''
                    end,
      login_time  = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then
                         case datediff(day, p.login_time, @now)
                              when 0
                              then convert(varchar(8), p.login_time, 8)
                              else convert(char(7), p.login_time, 12) +
                                   convert(varchar(8), p.login_time, 8)
                         end
                         else ''''
                    end,
      last_batch  = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then
                         case datediff(day, p.last_batch, @now)
                              when 0
                              then convert(varchar(8),
                                           p.last_batch, 8)
                              else convert(char(7), p.last_batch, 12) +
                                   convert(varchar(8), p.last_batch, 8)
                         end
                         else ''''
                    end,
      trn_start   = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1 and
                              t.trans_start is not null
                         then
                         case datediff(day, t.trans_start, @now)
                              when 0
                              then convert(varchar(8),
                                           t.trans_start, 8)
                              else convert(char(7), t.trans_start, 12) +
                                   convert(varchar(8), t.trans_start, 8)
                         end
                         else ''''
                    end,
      last_since  = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then str(p.last_since, 11, 3)
                         else ''''
                    end,
      trn_since   = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1 and
                              t.trans_since is not null
                         then str(t.trans_since, 11, 3)
                         else ''''
                    end,
      clr         = case when p.exec_context_id = 0 and p.isclr = 1
                         then ''clr''
                         else ''''
                    end,
      nstlvl      = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then coalesce(ltrim(str(p.nest_level)), '''')
                         else ''''
                    end,
      spid___     = p.spidstr,
      inputbuffer = case when p.exec_context_id = 0 and
                              coalesce(l.rowno, 1) = 1
                         then coalesce(i.inputbuffer, '''')
                         else ''''
                    end,
      current_sp  = coalesce(o1.object_name, ''''),
      curstmt     = case when coalesce(l.rowno, 1) = 1
                         then coalesce(p.current_stmt, '''')
                         else coalesce(substring(
                                    p.current_stmt, 1, 50), '''')
                    end,
      current_plan = case when p.exec_context_id = 0 and
                               coalesce(l.rowno, 1) = 1
                          then p.current_plan
                     end,
      hostprc     = case when coalesce(p.exec_context_id, 0) = 0 and
                              coalesce(l.rowno, 1) = 1
                         then ltrim(str(p.host_process_id))
                         else ''''
                    end,
      endpoint    = case when coalesce(p.exec_context_id, 0) = 0 and
                              coalesce(l.rowno, 1) = 1
                         then e.name
                         else ''''
                    end
from   @procs p
left   join #objects o1 on p.curdbid  = o1.database_id
                      and p.curobjid = o1.entity_id
left   join @inputbuffer i on p.session_id = i.spid
                         and p.exec_context_id = 0
left   join sys.endpoints e on p.endpoint_id = e.endpoint_id
left   join @transactions t on t.session_id = p.session_id
full   join (@locks l
           left join #objects o2 on l.database_id = o2.database_id
                                and l.entity_id   = o2.entity_id)
 on    p.session_id      = l.session_id
and    p.exec_context_id = 0
and    p.request_id      = 0
where  db_name() =  case when coalesce(l.rowno, 1) = 1 and
                              coalesce(p.exec_context_id, 0) = 0
                         then coalesce(db_name(p.request_dbid),
                                       db_name(p.proc_dbid))
                         else ''''
                    end
order by
        coalesce(p.session_id, l.session_id),
        p.exec_context_id, coalesce(nullif(p.request_id, 0), 99999999),
        l.rowno, lstatus,
        coalesce(o2.object_name, db_name(l.database_id)),
        l.rsc_type, l.rsc_description
option (keepfixed plan)

if @debug = 1 and @@nestlevel = 1
begin
   select @ms = datediff(ms, @now, getdate())
   raiserror (''completed, time %d ms.'', 0, 1, @ms) with nowait
end

if object_id(''sp__lock_help'') is null
    exec sp__printf ''-- missing "sp__lock_help"''
else
    exec sp__lock_help
goto ret

ret:
return @ret
end -- sp__lock_ex'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__lock_ex:

-- =============================================================== sp__lock_help
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__lock_help',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120905
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__lock_help') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__lock_help') with nowait
        goto skip_sp__lock_help
        end
    if @ver>120905
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__lock_help') with nowait
        goto skip_sp__lock_help
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__lock_help') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__lock_help'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__lock_help]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:process,status
    v:120905\s.zaglio: called by sp__lock and sp__lock_ex
*/
create proc sp__lock_help @opt sysname=null
as
begin
set nocount on
print ''
--- See also ---
sp__lock_ex     show grouped and detailed info
sp__perf        show most heavy running processes

--- Legend ---
Campi visualizzati:
    blocking    SPID del processo che viene bloccato da quello di riga
    n_locks     conto totale dei lock ad opera dello SPID di riga

Process status:
    dormant = SQL Server is resetting the session.
    running = The session is running one or more batches. When Multiple Active Result Sets (MARS) is enabled, a session can run multiple batches. For more information, see Using Multiple Active Result Sets (MARS).
    background = The session is running a background task, such as deadlock detection.
    rollback = The session has a transaction rollback in process.
    pending = The session is waiting for a worker thread to become available.
    runnable = The task in the session is in the runnable queue of a scheduler while waiting to get a time quantum.
    spinloop = The task in the session is waiting for a spinlock to become free.
    suspended = The session is waiting for an event, such as I/O, to complete.

Tipo di blocco:
    RID = Blocco su una sola riga di una tabella identificata da un identificatore di riga (RID).
    KEY = Blocco all''''interno di un indice che protegge un intervallo di chiavi in transazioni serializzabili.
    PAG = Blocco su una pagina di dati o di indice.
    EXT = Blocco su un extent, un''''unità di 8 pagine contigue.
    TAB = Blocco su un''''intera tabella, inclusi tutti i dati e gli indici.
    DB = Blocco su un database.
    FIL = Blocco su un file di database.
    APP = Blocco su una risorsa specifica di un''''applicazione.
    MD = Blocco su metadati o informazioni del catalogo.
    HBT = Blocco su un heap o un indice b-tree.
          Queste informazioni non sono complete in SQL Server 2005.
    AU = Blocco su un''''unità di allocazione.
         Queste informazioni non sono complete in SQL Server 2005.

Modalità di blocco richiesta. I possibili valori sono i seguenti:
    NULL = Non è concesso l''''accesso alla risorsa. Funge da segnaposto.
    Sch-S = Stabilità dello schema. Garantisce che nessun elemento dello schema,
            ad esempio una tabella o un indice, venga eliminato mentre
            in una sessione viene mantenuto attivo un blocco di stabilità
            dello schema sull''''elemento dello schema.
    Sch-M = Modifica dello schema. Deve essere impostato in tutte le sessioni
            in cui si desidera modificare lo schema della risorsa specificata.
            Assicura che nessun''''altra sessione faccia riferimento all''''oggetto
            specificato.
    S = Condiviso. La sessione attiva dispone dell''''accesso condiviso alla risorsa.
    U = Aggiornamento. Indica un blocco di aggiornamento acquisito su risorse
        che potrebbero venire aggiornate. Viene utilizzato per evitare una
        forma comune di deadlock che si verifica quando in più sessioni
        vengono bloccate risorse che potrebbero venire aggiornate
        in un momento successivo.
    X = Esclusivo. La sessione dispone dell''''accesso esclusivo alla risorsa.
    IS = Preventivo condiviso. Indica l''''intenzione di impostare blocchi
         condivisi (S) su alcune risorse subordinate nella gerarchia dei blocchi.
    IU = Preventivo aggiornamento. Indica l''''intenzione di impostare
         blocchi di aggiornamento (U) su alcune risorse subordinate
         nella gerarchia dei blocchi.
    IX = Preventivo esclusivo. Indica l''''intenzione di impostare
         blocchi esclusivi (X) su alcune risorse subordinate
     nella gerarchia dei blocchi.
    SIU = Condiviso preventivo aggiornamento. Indica l''''accesso condiviso
          a una risorsa con l''''intenzione di acquisire blocchi di
          aggiornamento su risorse subordinate nella gerarchia dei blocchi.
    SIX = Condiviso preventivo esclusivo. Indica l''''accesso condiviso
          a una risorsa con l''''intenzione di acquisire blocchi esclusivi
          su risorse subordinate nella gerarchia dei blocchi.
    UIX = Aggiornamento preventivo esclusivo.
          Indica un blocco di aggiornamento attivato su una risorsa
          con l''''intenzione di acquisire blocchi esclusivi su risorse
          subordinate nella gerarchia dei blocchi.
    BU = Aggiornamento di massa. Utilizzato dalle operazioni di massa.
    RangeS_S = Blocco condiviso intervalli di chiavi e risorsa.
               Indica una scansione di intervallo serializzabile.
    RangeS_U = Blocco condiviso intervalli di chiavi e aggiornamento risorsa.
               Indica una scansione di aggiornamento serializzabile.
    RangeI_N = Blocco inserimento intervalli di chiavi e risorsa Null.
               Utilizzato per verificare gli intervalli prima di inserire
               una nuova chiave in un indice.
    RangeI_S = Blocco conversione intervalli di chiavi.
               Creato da una sovrapposizione dei blocchi RangeI_N e S.
    RangeI_U = Blocco conversione intervallo di chiavi creato da
               una sovrapposizione di blocchi RangeI_N e U.
    RangeI_X = Blocco conversione intervallo di chiavi creato da
               una sovrapposizione di blocchi RangeI_N e X.
    RangeX_S = Blocco conversione intervallo di chiavi creato da
               una sovrapposizione di blocchi RangeI_N e RangeS_S.
    RangeX_U = Blocco conversione intervallo di chiavi creato da
               una sovrapposizione di blocchi RangeI_N e RangeS_U.
    RangeX_X = Blocco esclusivo intervalli di chiavi e risorsa.
               Si tratta di un blocco di conversione utilizzato
               quando viene aggiornata una chiave in un intervallo.

Stato della richiesta di blocco:
    CNVRT: è in corso la conversione del blocco da un''''altra modalità,
           ma la conversione è bloccata da un altro processo
           che mantiene attivo un blocco con una modalità in conflitto.
    GRANT: il blocco è stato ottenuto.
    WAIT: il blocco è bloccato da un altro processo che mantiene attivo un blocco con una modalità in conflitto.
''
end -- sp__lock_help'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__lock_help:

-- ===================================================================== sp__log
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__log',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131014
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__log') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__log') with nowait
        goto skip_sp__log
        end
    if @ver>131014
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__log') with nowait
        goto skip_sp__log
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__log') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__log'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__log]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,log
    v:131014\s.zaglio: d tag into o
    v:120926\s.zaglio: autogenerate of log table
    v:110510\s.zaglio: modified idx names into %__%
    r:110422\s.zaglio: test with md5
    r:110421\s.zaglio: generalized log table
    v:110316\s.zaglio: changed @txt to ntext
    v:110213\s.zaglio: added macro %host_name%
    v:110208\s.zaglio: added update
    v:100919.1105\s.zaglio: adapted to be used for generic I/O proc
    v:100919\s.zaglio: removed a dbg info
    v:100314\s.zaglio: fast&smart new log
    o:100314\s.zaglio: sp__trace
    t:sp__log #create_table#
    t:
        exec sp__log ''test''
        exec sp__log ''test sub (%s) log'',''test'',@n=10,@p1=''xx''
    t:
        declare @rid int
        exec sp__log @rid=''test'',@id=@rid out
        exec sp__log ''test sub log 1'',@rid
        select top 10 * from [log] order by id desc
    t: sp__log_show ''%''
    t: drop table log
*/
CREATE proc sp__log
    @txt nvarchar(4000) =null,
    @ref sysname        =null,
    @n money            =null,
    @m money            =null,
    @id int             =null out,
    @pid int            =null,
    @p1 sql_variant     =null,
    @p2 sql_variant     =null,
    @p3 sql_variant     =null,
    @p4 sql_variant     =null,
    @opt sysname        =null
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0,
       @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if object_id(''log'',''U'') is null
    begin
    exec sp__printf ''-- autogenerate log table''
    exec(''
        create table log(
            id  int identity,
            rid int null,                   -- refer to parent
            pid int null,                   -- refer to extarnal item
            [key] as substring(c1,1,256),   -- guid or tbl.fld
            c1     varbinary(8000) null,    -- txt or msg
            c2     varbinary(256) null,     -- spid
            c3     varbinary(256) null,     -- n or table
            c4     varbinary(256) null,     -- m or field
            dt     datetime not null
            )

        alter table dbo.[log] add constraint pk__log primary key (id desc)
        create index ix__log_rid on [log](rid,id)    -- drop index [log].ix__log_rid
        create index ix__log_key on [log]([key])     -- drop index [log].ix__log_key
        create index ix__log_dt on [log](dt desc)    -- drop index [log].ix__log_dt
    '')
    end

if @txt is null and @ref is null and @id is null goto help

declare
    @log nvarchar(4000),@vb_log varbinary(8000),
    @chk bit

select
    @log=substring(@txt,1,4000),
    @chk =charindex(''|chk|'',@opt)

declare @rid int,@st sysname

if not @log is null
    begin
    select @log=replace(@log,''@@host_name'',host_name())
    select @log=replace(@log,''@@system_user'',system_user)
    select @st=convert(sysname,@@trancount)
    select @log=replace(@log,''@@trancount'',@st)
    end

if not @p1 is null
    select @log=dbo.fn__printf(@log,@p1,@p2,@p3,@p4,
                               null,null,null,null,null,null)

-- get binary version
select
    @vb_log=cast(@log as varbinary(8000))

-- if a reference is specified ...
if not @ref is null
    begin
    if isnumeric(@ref)=1
        select @rid=convert(int,@ref)
    else
        begin
        -- ... find the id and ...
        select top 1 @rid=id from [log] with (nolock)
        where c1=@ref order by id desc
        -- ... if not exists, add it
        if @rid is null exec sp__log @ref,@id=@rid out
        end
    end -- get@rid

-- example: sp__log @ref=''test'',@id=@rid out
-- if nothing to log but @ref, return the id
if @log is null and not @ref is null
    begin
    select @id=@rid
    goto ret
    end

-- example: sp__log @hash,@ref=test,@id=@id out,@opt=chk
-- if CHK option, verify if already exists @log or @ref
if @chk=1 and not @log is null and not @rid is null
    begin
    if exists(select null from [log] with (nolock)
           where [key]=substring(@vb_log,1,256)
           and rid=@rid
           )
        begin
        select @id=null
        goto ret
        end
    end -- chk

-- example: sp__log ''sub log info 1 chg'',''test'',@id=@id -- not null id
-- ================================================================== act_upd ==
if not @id is null
    update [log] set
        pid=isnull(@pid,pid),
        c1=isnull(@vb_log,c1),
        c3=isnull(@n,c3),
        c4=isnull(@m,c4)
    where id=@id

-- example: sp__log ''test'',@id=@rid out
-- examples: sp__log ''sub log info 1'',''test'',  @id=@id out  -- id must be null
-- ================================================================== act_ins ==
if @id is null
    begin
    if @log!=''#src''     -- single log
        begin
        insert [log](   rid,   pid,
                        c1,
                        c2, c3, c4,     dt)
        select          @rid,  @pid,
                        @vb_log as c1,
                        @@spid, @n, @m, getdate()
        select @id=@@identity
        end
    else
        begin           -- log from table #src
        if @rid is null goto err_src
        insert [log](   rid,   pid,
                        c1,
                        c2, c3, c4,     dt)
        select          @rid,  @pid,
                        cast(src.line as varbinary(8000)) as c1,
                        @@spid, @n, @m, getdate()
        from #src src
        order by lno
        end
    end -- if @id is null

-- exec sp__printf ''rid:%s ref:%s'',@rid,@ref

goto ret
-- =================================================================== errors ==
err_src:    exec @ret=sp__err ''#src insert require a reference'' goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    register info into table "log"

Parameters
    @txt    text to log
            Some macros are replaced:
                %s,%d   with @p1,@p2,@p3,@p4
                @@host_name, @@system_user, @@trancount with replative mssql functions
            if #src, copy line by line the #src content (@ref is required)

    @ref    is used for children of previous returned @id
            (if is a string, search the relative id)
    @id     if null, return the new id (used then as @ref)
            if not null, update the relative record (normally one of @n,@m field)
    @opt    options
            chk     if @ref and @txt already exists, null the @id

Notes
    This sp require the "log" table

Examples
    exec sp__log "fast raw log"

    -- structured log
    declare @id int, @rid int
    exec sp__log "test",@id=@rid out                      -- ins
    select "ref id="+cast(@rid as sysname)
    exec sp__log "sub log info 1","test",  @id=@id out    -- attach 1 by ref and get id

    exec sp__log @ref="test",@id=@rid out                 -- get ref id
    exec sp__log "sub log info 2'''',@rid                    -- attach 2 by rid
    exec sp__log "%d , %s", @rid, @p1=12, @p2="XX"        -- parametrized

    exec sp__log "sub log info 1 chg","test",@id=@id      -- upd

    -- show last 10 logs
    exec sp__log_view #10

    -- store content of #src (run 2 times)
    declare @id int, @hash binary(16)
    create table #src(lno int identity,line nvarchar(4000))
    insert #src(line) select top 3 [name] from sysobjects order by id
    exec sp__md5 @hash out
    exec sp__log @hash,@ref=test,@id=@id out,@opt=chk
    if not @id is null
        begin
        exec sp__log #src,@ref=@id
        exec sp__log_view #10           -- show last 10 logs
        exec sp__log_view @hash,test    -- show specific log
        end
    else print "already_stored"
    drop table #src

''

ret:
return @ret
end -- sp__log'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__log:

-- =============================================================== sp__log_trace
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__log_trace',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130605
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__log_trace') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__log_trace') with nowait
        goto skip_sp__log_trace
        end
    if @ver>130605
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__log_trace') with nowait
        goto skip_sp__log_trace
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__log_trace') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__log_trace'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__log_trace]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130605\s.zaglio: removed printf deprecated parameters
    v:110314\s.zaglio: renamed from deprecated sp__trace; use sp__log
    v:100205\s.zaglio: deprecated fn__injectN to fn__inject and added help
    v:090603\S.Zaglio: added @elapsed and %e
    v:090131\S.Zaglio: removed replace if @p? are all null
    v:090129\S.Zaglio: added @privacy
    v:090128\S.Zaglio: forced on @print to match correclty when error and replaced | with , in params
    v:090121\S.Zaglio: added outbuffer trace when @txt=''sp__outbuffer''
    v:090117\S.Zaglio: added @force to sp_prinftf
    v:081223\S.Zaglio: added multilevel @dbg
    v:081215\S.Zaglio: a bug when print. Date style to 126
    v:081203\S.Zaglio: added @print, removed @trace from sp__printf
    v:081129\S.Zaglio: set erf_id as relative to simplify export to other server
    v:081120\S.Zaglio: added @last_id and @ref_id and @proc. Old compatible and auto add new log_trace col
    v:081030\S.Zaglio: convert in nvarchar (be careful with 4000 chars and '' inside)
    v:081014\S.Zaglio: added %t as replacer for iso date
    v:080925\S.Zaglio: added privacy
    v:080922\S.Zaglio: added @txt as numeric of @@error, replaced with sysmessages
    v:080918\S.Zaglio: added @trace_once out to simplify loop tracing (run only once)
    v:080628\S.Zaglio: fast log for debugging
    t:
        begin
        declare @txt nvarchar(4000) set @txt=''12345''+space(3800)+'' after 3800 spaces:67890''+space(85)+''abcdefghi''
        exec sp__trace ''first run fs'',@init=1
        exec sp__trace @txt,@proc=''sp_hello''
        exec sp__trace ''add %s'',''element''
        exec sp__trace ''added %d'',12
        -- test long comments
        select dbo.fn__str_simplify(txt,default) as txt from log_trace where len(txt)>1000
        exec sp__trace -- print
        exec sp__trace ''second run fs'',@init=1
        exec sp__trace ''add %s'',''element''
        exec sp__trace ''added ''''%d'''''',12
        exec sp__trace -- print
        exec sp__trace @clean=1
        -- printf test/debugging
        exec sp__trace ''printf test'',@init=1
        exec sp__trace ''im here'',@print=1
        exec sp__trace -- print
        exec sp__trace @clean=1
        select dbo.fn__str_simplify(txt,default) as txt from log_trace where len(txt)>1000

        end
    t: sp__trace ''select x from opendatasource(datas,uid,password=xyz) where ...'' select * from log_trace where id=(select max(id) from log_trace)
    t: -- test @ref_id,@last_id,@proc features
        begin
        truncate table log_trace
        declare @ref_id int
        exec sp__trace ''test for old compatibuility'',@clean=1
        exec sp__trace ''parent'',@last_id=@ref_id out
        exec sp__trace ''child1'',@ref_id=@ref_id
        exec sp__trace ''child2'',@ref_id=@ref_id
        exec sp__trace ''this is a new feature'',@proc=''mySP''
        exec sp__trace
        exec sp__trace @clean=1
        end
*/
CREATE proc [dbo].[sp__log_trace]
    @txt nvarchar(4000)=null,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null,
    @p5 sql_variant=null,
    @p6 sql_variant=null,
    @p7 sql_variant=null,
    @p8 sql_variant=null,
    @init bit=0,
    @clean bit=0,
    @trace bit=1,                  -- spare to write on each call: if @trace=1 exec sp__trace ...
    @trace_once bit=1 out,         -- spare to write on each call: if @trace=1 exec sp__trace ...
    @last_id int=null out,         -- return last @@identity
    @ref_id int=null,
    @proc sysname=null,            -- short cut to %t|@proc|@txt format
    @print bit=0,
    @privacy bit=0,
    @elapsed datetime=null out,
    @dbg smallint=0
as
begin
declare @now datetime
declare @ms int
if not @elapsed is null
    begin
    set @now=getdate()
    set @ms=datediff(ms,@elapsed,@now)
    set @txt=replace(@txt,''%e'',convert(nvarchar(48),@ms))
    set @elapsed=@now
    end

declare @sql nvarchar(4000)
declare @sql1 nvarchar(4000)
if @txt=''sp__outbuffer'' begin set @txt='''' exec sp__outputbuffer @txt out end

if @dbg<0 begin exec sp__printf ''@@nestlevel=%d'',@@nestlevel end
set nocount on

if isnumeric(@txt)=1 begin
    if exists(select top 1 error from master.dbo.sysmessages where error=convert(int,@txt))
        select top 1 @txt=description from master.dbo.sysmessages where error=convert(int,@txt)
end
if @init=1 or @clean=1
    begin
    if dbo.fn__exists(''log_trace'',''U'')=0
        begin
        set @sql=''create table log_trace (id int identity, ref_id int, spid int, txt nvarchar(4000))''
        exec sp__printf ''-- log_trace created''
        exec(@sql)
        end -- create
    else
        begin
        if @proc is null set @sql=''delete from log_trace where spid=''+convert(nvarchar(32),@@spid)
        else set @sql=''delete from log_trace where dbo.fn__str_at(txt,''''|'''',2)=''+@proc
        exec(@sql)
        end
    if @txt is null goto ret
    end -- @init=1

if @txt is null and @clean=0 goto help

if not @proc is null set @txt=''%t|''+@proc+''|''+left(@txt,4000-20-len(@proc))
set @txt=replace(@txt,''%t'',convert(nvarchar(48),getdate(),126))
if not @p1 is null or not @p2 is null or not @p3 is null or not @p4 is null or
   not @p5 is null or not @p6 is null or not @p7 is null or not @p8 is null
    begin
    set @p1=replace(convert(nvarchar(4000),@p1),''|'','','') set @p2=replace(convert(nvarchar(4000),@p2),''|'','','')
    set @p3=replace(convert(nvarchar(4000),@p3),''|'','','') set @p4=replace(convert(nvarchar(4000),@p4),''|'','','')
    set @p5=replace(convert(nvarchar(4000),@p5),''|'','','') set @p6=replace(convert(nvarchar(4000),@p6),''|'','','')
    set @p7=replace(convert(nvarchar(4000),@p7),''|'','','') set @p8=replace(convert(nvarchar(4000),@p8),''|'','','')
    set @txt=dbo.fn__printf(@txt,@p1,@p2,@p3,@p4,@p5,@p6,@p7,@p8,null,null)
    end
if left(ltrim(@txt),7) in (''select '',''insert '',''delete '',''update '',''execute'',''exec @r'') set @txt=dbo.fn__str_simplify(@txt,default)
if @privacy=1 begin
    if @txt like ''%OPENDATASOURCE%(%password%)%'' begin -- kepp privacy
        declare @i int,@j int
        set @i=charindex(''opendatasource'',@txt)
        if @i>0 set @i=charindex(''('',@txt,@i)
        set @j=charindex('')'',@txt,@i)
        if @j<1 set @j=len(@txt)
        if @i>0 set @txt=substring(@txt,1,@i)+''***privacy***''+substring(@txt,@j,len(@txt))
    end
end -- privacy
if @print=1 begin
    if coalesce(@proc,'''')='''' set @sql=@txt
    else set @sql=dbo.fn__str_at(@txt,''|'',3)
    exec sp__printf @sql
    end
if @trace=0 or @trace_once=0 goto ret
set @trace_once=0                  -- disable trace in loop (the caller must use out to enable this)
if @ref_id is null begin
    set @sql=''insert into log_trace(spid,txt) values(''+convert(nvarchar(32),@@spid)+'',''''''
    set @sql1=dbo.fn__inject(@txt)
    if abs(@dbg)>=@@nestlevel exec sp__printf ''%s%s'''')'',@sql,@sql1
    exec(@sql+@sql1+'''''')'')
    end
else
    begin
    set @sql=''insert into log_trace(ref_id,spid,txt) values(''+convert(nvarchar(32),@ref_id)+''-ident_current(''''log_trace''''),''+convert(nvarchar(32),@@spid)+'',''''''
    set @sql1=dbo.fn__inject(@txt)
    if abs(@dbg)>=@@nestlevel exec sp__printf ''%s%s'''')'',@sql,@sql1
    exec(@sql+@sql1+'''''')'')
    if @@error<>0 begin
        exec(''alter table log_trace add ref_id int'')
        exec(@sql+@sql1+'''''')'')
        end
    end
set @last_id=ident_current(''log_trace'') -- @@identity
goto ret

help:
select @sql =''\nparameters:\n''
            +''\t@init\t\t1 create log_trace\n''
            +''\nsamples:\n''
            +''\tsp__trace ''''test''''''
exec sp__usage ''sp__trace'',@sql

ret:
end -- [sp__log_trace]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__log_trace:

-- ======================================================== sp__log_trace_search
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__log_trace_search',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130225
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__log_trace_search') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__log_trace_search') with nowait
        goto skip_sp__log_trace_search
        end
    if @ver>130225
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__log_trace_search') with nowait
        goto skip_sp__log_trace_search
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__log_trace_search') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__log_trace_search'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__log_trace_search]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130225\s.zaglio: adapted to new sp__udage
    v:110314\s.zaglio: renamed from deprecated sp__trace_search (see sp__log)
    v:090322\S.Zaglio: search into formatted log_trace
*/
CREATE proc [dbo].[sp__log_trace_search]
    @proc sysname=null,
    @days int=1,
    @id smallint=null,
    @like sysname=null
as
begin
set nocount on
declare @sql nvarchar(4000)

if @proc is null and (@id is null)
    begin
    select @sql =''select top 100 percent dbo.fn__str_at(txt, ''''|'''', 2) as prc ''
                +''from dbo.log_trace l with (readpast) ''
                +''where substring(txt,24,1)=''''|'''' group by dbo.fn__str_at(txt, ''''|'''', 2)''
    exec(@sql)
    exec sp__usage ''sp_log_trace'',@extra=''\n    @proc can be % or a procedure\n    @like is used in like %@like%''
    goto ret
    end

select top 100 percent id, ref_id + id-1 as rel_id, spid,
convert(datetime,substring(txt,1,23)) date,
dbo.fn__str_at(txt, ''|'', 2) as prc,
replace(dbo.fn__str_at(txt, ''|'', 3),char(13)+char(10),'' '') as txt
from dbo.log_trace l with (readpast)
where
    substring(txt,24,1)=''|''
and
    (@days is null or convert(datetime,substring(txt,1,23))>getdate()-@days)
and
    dbo.fn__str_at(txt, ''|'', 2) like ''%''+isnull(@proc,'''')+''%''
and
    (@id is null or [id]=@id)
and
    (@like is null or txt like ''%''+@like+''%'')
order by id desc
ret:
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__log_trace_search:

-- ================================================================ sp__log_view
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__log_view',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131014
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__log_view') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__log_view') with nowait
        goto skip_sp__log_view
        end
    if @ver>131014
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__log_view') with nowait
        goto skip_sp__log_view
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__log_view') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__log_view'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__log_view]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    v:131014\s.zaglio: d tag adjust
    v:121107\s.zaglio: a bug near @n of keep (now -@n)
    v:121105\s.zaglio: added KEEP option
    v:110513\s.zaglio: removed readpast
    v:110509\s.zaglio: removed out to #src because conflict !!!!
    v:110506\s.zaglio: adapted too new sp__log
    v:110505\s.zaglio: renamed into sp__log_view
    v:110504\s.zaglio: added md5 recordnize
    v:110422\s.zaglio: adapted to new log format
    v:100314\s.zaglio: search in log
    d:100314\s.zaglio: sp__log_show
    d:100314\s.zaglio: sp__log_search
    d:100314\s.zaglio: sp__trace_search
    t:
        exec sp__log ''1st test'',@key=''sp__log_search''
        exec sp__log ''1st sub test'',@key=''sp__log_search''
    t:sp__log_view 12
    t:sp__log_view @what=null,@ref=2
    t:sp__log_view #10
*/
CREATE proc [dbo].[sp__log_view]
    @what sql_variant=null,
    @ref sql_variant=null,
    @opt sysname=null,
    @dbg int=null
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0,@dbg=isnull(@dbg,0),
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
if @what is null and @ref is null goto help

declare
    @key varbinary(256),@txt nvarchar(4000),@rid int,
    @bt varchar(32),@bk varchar(32),@id int,@top int,
    @keep bit,@n int,@dt datetime,@tdt nvarchar(4),
    @sql nvarchar(4000),@swhat nvarchar(4000),@sref nvarchar(128)

select
    @bk=cast(sql_variant_property(@what,''BaseType'') as varchar),
    @bt=cast(sql_variant_property(@ref,''BaseType'') as varchar),
    @top=10000,@dt=getdate(),@swhat=cast(@what as nvarchar(4000)),
    @sref=cast(@ref as nvarchar(128))

if isnumeric(@sref)=1
    select @rid=cast(@sref as int)
else
    select top 1 @rid=id
    from [log] with (nolock)
    where [key]=@sref
    order by id desc

if @swhat like ''%[dhwm]'' and isnumeric(left(@swhat,len(@swhat)-1))=1
    begin
    if @rid is null goto err_ref
    select @n=left(@swhat,len(@swhat)-1),@tdt=right(@swhat,1)+right(@swhat,1)
    select @sql=''select @dt=dateadd(''+@tdt+'',-@n,@dt)''
    exec sp_executesql @sql,N''@dt datetime out,@n int'',@dt=@dt out,@n=@n
    delete from [log] where rid=@rid and dt<@dt
    select @n=@@rowcount
    if @dbg>0 exec sp__printf ''-- delete %d records before %s'',@n,@dt
    goto ret
    end -- keep

-- create table #src(lno int identity, line nvarchar(4000))
-- select * from #src drop table #src
-- example: sp__log_view 0x3db54b02d3fa8a637c68bea25398155e,test
if @bk in (''binary'',''varbinary'')
    begin
    if @ref is null goto err_ref
    -- select * from log where [key]=cast(''test'' as sql_variant)
    -- select * from log where [key]=cast(cast(''test'' as sql_variant) as nvarchar(256))
    end
if @bk in (''int'') select @id=cast(@what as int)
if @bk in (''nvarchar'',''varchar'')
    begin
    select @txt=cast(@what as nvarchar(4000))
    select @key=cast(@what as varbinary(256))
    if left(@txt,1)=''#'' and isnumeric(substring(@txt,2,4000))=1
        select  @top=cast(substring(@txt,2,4000) as int),
                @txt=null, @key=null
    else
        select @top=1000
    if charindex(''%'',@txt)>0 select @key=null
    end

-- if @dbg=1 select @top,@id,@rid,@txt,@key
select top (@top)
-- select
    id, rid, dt,
    [key],
    convert(nvarchar(4000),c1) as txt,
    convert(money,c3) as n,
    convert(money,c4) as m,
    convert(int,c2) as [spid]
from [log] with (nolock)
where 1=1
and (@id is null or id=@id)
and (@rid is null or rid=@rid)
and (@key is null or [key]=@key)
and (@txt is null or cast(c1 as nvarchar(4000)) like @txt)
order by dt desc

goto ret

-- =================================================================== errors ==
err_ref:    exec @ret=sp__err ''a reference not null and correct is required''
            goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    show log info

Parameters
    @what   if numeric is the log id
            if hexadecimal is the key
            if contain % search lines like @what
            if begin with #nnn show the first nnn rows
            if end with D,H,W,M (days,hours,weeks,months)
            and @opt has "shrink", keep only lasts @what(DHWM) records
    @ref    if present become the parent of the key group
            can be the parent id
    @opt    options
            keep    delete older logs if @what like %[dhwm]

Examples
    -- show last 100 logs
    sp__log_view #100
    -- show logs of this parent key
    sp__log_view 0x9c14ce8040deb2804c7c3bc111cc10dd,test
    -- show by id
    sp__log_view 12

    sp__log_view "sys%"

    sp__log_view "15d","SP_MYPROC",@opt="keep"

''

select distinct
    case
    when substring(cast([key] as nvarchar),1,1) like ''[a-zA-Z0-9]''
    then convert(nvarchar(256),[key])
    else dbo.fn__hex([key])
    end as [key]
into #keys
from [log]

exec sp__select_astext ''select * from #keys''


select @ret=-1

ret:
return @ret
end -- sp__log_view'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__log_view:

-- ==================================================================== sp__loop
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__loop',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__loop') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__loop') with nowait
        goto skip_sp__loop
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__loop') with nowait
        goto skip_sp__loop
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__loop') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__loop'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__loop]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:091018\s.zaglio: loop into values, replacing in the template and execute or print it
*/
CREATE proc [dbo].[sp__loop]
    @tpl nvarchar(4000)=null,
    @x nvarchar(4000)=null,
    @y nvarchar(4000)=null,
    @z nvarchar(4000)=null,
    @parallel bit=1,
    @simul bit=0
as
begin
print ''todo: see inside''
/*
exec sp__loop ''
-- test inner temp tables
create proc sp_temp_test<x>
as
begin
create table #test(<y>)
select id,right(name,13) from tempdb..sysobjects where id=object_id(''''tempdb..#test'''')
exec sp_temp_test<z> -- <onlast:remove>
drop table #test
end
g o
drop proc sp_temp_test<x> --<onlast:addall>
''
@x=''1,2,3'',
@y=''id1 int|id1 real,p2c2 money,p2c3 text|id1 nvarchar(10),c2 int'',
@z=''2,3,4'',
@parallel=1,
@simul=1
*/
-- must return:
/*
create proc sp_temp_test2
as
begin
create table #test(id1 real,p2c2 money,p2c3 ntext)
select id,right(name,13) from tempdb..sysobjects where id=object_id(''tempdb..#test'')
exec sp_temp_test3
drop table #test
end
g o
create proc sp_temp_test3
as
begin
create table #test(id1 nvarchar(10),c2 int)
select id,right(name,13) from tempdb..sysobjects where id=object_id(''tempdb..#test'')
drop table #test
end
g o
drop proc sp_temp_test1
drop proc sp_temp_test2
drop proc sp_temp_test3
*/
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__loop:

-- ========================================================= sp__maint_idxdefrag
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__maint_idxdefrag',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110923
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__maint_idxdefrag') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__maint_idxdefrag') with nowait
        goto skip_sp__maint_idxdefrag
        end
    if @ver>110923
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__maint_idxdefrag') with nowait
        goto skip_sp__maint_idxdefrag
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__maint_idxdefrag') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__maint_idxdefrag'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__maint_idxdefrag]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:index,maintenance,rebuild,fragmentation
    v:110923\s.zaglio: refined
    r:091018.1000\s.zaglio:  defrag current db indexes
    t:sp__maint_idxdefrag run
*/
CREATE proc sp__maint_idxdefrag
    @opt sysname=null
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0,@opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- declare variables
set nocount on
declare @tablename varchar (128)
declare @execstr   varchar (255)
declare @objectid  int
declare @indexid   int
declare @frag      decimal
declare @maxfrag   decimal
declare @run       bit

select @run=charindex(''|run|'',@opt)
if @run=0 exec sp__printf ''-- use @opt=''''run'''' to execute it isntead of show''

-- decide on the maximum fragmentation to allow
select @maxfrag = 8

-- declare cursor
declare tables cursor for
   select ''[''+table_schema+''].[''+table_name+'']'' as table_name
   from information_schema.tables
   where table_type = ''base table'' and table_name not like ''t%''

-- create the table
create table #fraglist (
   objectname char (255),
   objectid int null,
   indexname char (255),
   indexid int null,
   lvl int null,
   countpages int null,
   countrows int null,
   minrecsize int null,
   maxrecsize int null,
   avgrecsize int null,
   forreccount int null,
   extents int null,
   extentswitches int null,
   avgfreebytes int null,
   avgpagedensity int null,
   scandensity decimal,
   bestcount int null,
   actualcount int null,
   logicalfrag decimal,
   extentfrag decimal null)

-- open the cursor
open tables

-- loop through all the tables in the database
fetch next
   from tables
   into @tablename

while @@fetch_status = 0
begin
-- do the showcontig of all indexes of the table
   exec sp__printf ''get info of %s'',@tablename
   insert into #fraglist
   exec (''dbcc showcontig ('''''' + @tablename + '''''')
      with fast, tableresults, all_indexes, no_infomsgs'')
   fetch next
      from tables
      into @tablename
end

-- close and deallocate the cursor
close tables
deallocate tables

if @run=0
    begin
    select  case when logicalfrag >= @maxfrag
            then ''*'' else '''' end [!],objectname,indexname,logicalfrag,countrows
    from #fraglist
    where indexproperty (objectid, indexname, ''indexdepth'') > 0
    order by [!] desc,logicalfrag desc
    select @ret=-1
    end
else
    begin
    -- declare cursor for list of indexes to be defragged
    declare indexes cursor for
       select objectname, objectid, indexid, logicalfrag
       from #fraglist
       where logicalfrag >= @maxfrag
          and indexproperty (objectid, indexname, ''indexdepth'') > 0

    -- open the cursor
    open indexes

    -- loop through the indexes
    fetch next
       from indexes
       into @tablename, @objectid, @indexid, @frag

    while @@fetch_status = 0
    begin
       print ''executing dbcc indexdefrag (0, '' + rtrim(@tablename) + '',
          '' + rtrim(@indexid) + '') - fragmentation currently ''
           + rtrim(convert(varchar(15),@frag)) + ''%''
       select @execstr = ''dbcc indexdefrag (0, '' + rtrim(@objectid) + '',
           '' + rtrim(@indexid) + '')''
       exec (@execstr)

       fetch next
          from indexes
          into @tablename, @objectid, @indexid, @frag
    end

    -- close and deallocate the cursor
    close indexes
    deallocate indexes
    end

-- delete the temporary table
drop table #fraglist
return @ret
end -- sp__maint_idxdefrag'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__maint_idxdefrag:

-- ========================================================== sp__maint_idxstats
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__maint_idxstats',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=111116.1040
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__maint_idxstats') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__maint_idxstats') with nowait
        goto skip_sp__maint_idxstats
        end
    if @ver>111116.1040
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__maint_idxstats') with nowait
        goto skip_sp__maint_idxstats
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__maint_idxstats') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__maint_idxstats'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__maint_idxstats]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:index,optimization,statistic
    v:111116.1040\s.zaglio: use mssql perf. sp to list indexes to add
*/
CREATE proc sp__maint_idxstats
    @obj sysname = null
as
begin
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0
if @obj is null goto help

declare @db sysname,@sql nvarchar(max)
select @db=db_name()

if @obj=''#'' select @obj=null

-- Find missing indices on your server
select
    parsename(statement,3) db,
    parsename(statement,1) obj,
    equality_columns+isnull('',''+inequality_columns,''''),
    ''create index ''
    +upper(replace(
        ''idx_perf_''
        + case when equality_columns is null then ltrim('''') else  replace(rtrim(ltrim(replace(replace(replace(equality_columns,'','',''_''),''['',''''),'']'',''''))),char(32), ''_'')  end
        + case when inequality_columns is null then ltrim('''') else replace(rtrim(ltrim(replace(replace(replace(inequality_columns,'','',''_''),''['',''''),'']'',''''))),char(32), ''_'') end
        + ''_''+cast(mid.index_handle as varchar)
        ,''__'',''_''))
    + '' on '' + statement + '' (''
    + case when equality_columns is null then '''' else  replace(replace(equality_columns,''['',''''),'']'','''') end
    + case when inequality_columns is null then '')'' else '''' end
    + case when inequality_columns is null then '''' else + (case when equality_columns is null then '''' else  '','' end) + inequality_columns + '')'' end
    + case when included_columns is null then '''' else + '' include('' + replace(replace(included_columns,''['',''''),'']'','''') + '')''end
from sys.dm_db_missing_index_details mid
inner join sys.dm_db_missing_index_groups mig on mig.index_handle = mid.index_handle
inner join sys.dm_db_missing_index_group_stats migs on mig.index_group_handle = migs.group_handle
where (@obj is null or @obj=parsename(statement,1))
order by
    parsename(statement,3),parsename(statement,1),
    avg_total_user_cost * avg_user_impact * (user_seeks + user_scans) desc;


--- Find unused indices in your database
select @sql = ''use '' + quotename(@db)
select @sql = @sql + '';
select distinct
    object_name(sis.object_id) tablename,
    si.name as indexname,
    sc.name as columnname,
    sic.index_id,
    sis.user_seeks,
    sis.user_scans,
    sis.user_lookups,
    sis.user_updates
from sys.dm_db_index_usage_stats sis
inner join sys.indexes si
    on sis.object_id = si.object_id
    and sis.index_id = si.index_id
    and (si.is_disabled = 0
    and si.is_primary_key=0)
inner join sys.index_columns sic
    on sis.object_id = sic.object_id
    and sic.index_id = si.index_id
inner join sys.columns sc
    on sis.object_id = sc.object_id
    and sic.column_id = sc.column_id
where
    sis.database_id = db_id(@db)
    and (user_seeks = 0 and user_scans=0 and user_lookups=0)
    and  si.type=2 and si.is_unique = 0 and si.is_primary_key = 0 and is_disabled = 0
    and (@obj is null or @obj=object_name(sis.object_id))
order by tablename
''
exec sp_executesql @sql,N''@db sysname, @obj sysname'',@db=@db,@obj=@obj
goto ret

help:
exec sp__usage @proc,''
Scope
    use mssql perf. sp to list indexes to add

Parameters
    @obj    object to filter for indexes or # for all
''

ret:
return @ret
end -- sp__maint_idxstats'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__maint_idxstats:

-- ===================================================================== sp__man
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__man',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131013
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__man') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__man') with nowait
        goto skip_sp__man
        end
    if @ver>131013
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__man') with nowait
        goto skip_sp__man
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__man') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__man'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__man]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:131013\s.zaglio: show manual of object of search by keywords
    t:sp__man soap#call#web#service
*/
CREATE proc sp__man
    @what sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare @sep char
-- =========================================================== initialization ==
if charindex(''#'',@what)>0 select @sep=''#'' else select @sep='',''
-- ======================================================== second params chk ==
if nullif(@what,'''') is null goto help

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

;with
    keywords(kword) as (
        select case when right(token,1)=''s'' then left(token,len(token)-1) else token end as token
        from fn__str_split(@what,@sep) a
    ),
    subkeys(obj_id,obj,match) as (
        select object_id as obj_id,name,1
        from sys.objects
        cross apply fn__str_split(name,''_'')
        where token in (select kword from keywords)
    ),
    matches(obj,n) as (
        select
            o.name,
            case when charindex(kws.kword,definition)>1 then 1 else 0 end match
        from sys.objects o
        join sys.sql_modules m
        on o.object_id=m.object_id
        cross apply keywords kws
    ) -- select * from matches order by obj desc,n desc
-- select obj,n from matches union all select obj,1 from subkeys order by n desc
,
    matched(obj,n) as (
        select obj,sum(n) n
        from (
            select obj,n from matches
            union all
            select obj,1 from subkeys
            ) md
        group by obj
    )
select top 5 *
from matched
order by n desc

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    show manual of object or search by keywords

Notes
    actually not really uses k tag info but search into all definition

Parameters
    [param]     [desc]
    @what       specific object or list of keywords separated by # or comma
    @opt        (not used)
    @dbg        (not used)

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__man'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__man:

-- ===================================================================== sp__md5
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__md5',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110429
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__md5') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__md5') with nowait
        goto skip_sp__md5
        end
    if @ver>110429
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__md5') with nowait
        goto skip_sp__md5
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__md5') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__md5'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__md5]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110429\s.zaglio: specified for #src
    v:080808\S.Zaglio: calculate hash md5 of multiple strings
*/
CREATE proc sp__md5
    @hash binary(16) =null out,
    @skip int=null
as
begin
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

if object_id(''tempdb..#src'') is null goto help

declare
    @buffer varbinary(64), @counter int,@line nvarchar(4000),
    @a bigint, @b bigint, @c bigint, @d bigint,@len int

select
    @skip=isnull(@skip,0),
    @a = 0x67452301, @b = 0xefcdab89, @c = 0x98badcfe, @d = 0x10325476

declare cs cursor local for
    select isnull(line,'''')
    from #src
    order by lno
open cs
while 1=1
    begin
    fetch next from cs into @line
    if @@fetch_status!=0 break
    if @skip>0
        begin
        select @skip=@skip-1
        continue
        end

    select @counter=1,@hash=0,@len=datalength(@line)

    -- exec sp__printf ''@l=%d, @h=%d, @line=%s'',@len,@hash,@line

    while @counter<=@len or @len=0
        begin

        select @buffer=substring(cast( @line as varbinary(8000) ),@counter,55)
        select @counter=@counter+55


        select @buffer = @buffer
                     + 0x80 +  cast( replicate( 0x00, 64 - 8 - 1 - datalength( @buffer )  ) as varbinary(64) )
                     + cast( (datalength( @buffer )*8 & 0xFF) as binary(1) )
                     + cast( (datalength( @buffer )*8 & 0xFF00)/256 as binary(1) )
                     + 0x0

        select @a = dbo.fn__md5_r(0, @a, @b, @c, @d, cast( reverse( substring( @buffer, 00 + 1, 4 ) ) as binary(4) ), 128,    0xd76aa478 )
        select @d = dbo.fn__md5_r(0, @d, @a, @b, @c, cast( reverse( substring( @buffer, 04 + 1, 4 ) ) as binary(4) ), 4096,   0xe8c7b756 )
        select @c = dbo.fn__md5_r(0, @c, @d, @a, @b, cast( reverse( substring( @buffer, 08 + 1, 4 ) ) as binary(4) ), 131072, 0x242070db )
        select @b = dbo.fn__md5_r(0, @b, @c, @d, @a, cast( reverse( substring( @buffer, 12 + 1, 4 ) ) as binary(4) ), 4194304,0xc1bdceee )

        select @a = dbo.fn__md5_r(0, @a, @b, @c, @d, cast( reverse( substring( @buffer, 16 + 1, 4 ) ) as binary(4) ), 128,    0xf57c0faf )
        select @d = dbo.fn__md5_r(0, @d, @a, @b, @c, cast( reverse( substring( @buffer, 20 + 1, 4 ) ) as binary(4) ), 4096,   0x4787c62a )
        select @c = dbo.fn__md5_r(0, @c, @d, @a, @b, cast( reverse( substring( @buffer, 24 + 1, 4 ) ) as binary(4) ), 131072, 0xa8304613 )
        select @b = dbo.fn__md5_r(0, @b, @c, @d, @a, cast( reverse( substring( @buffer, 28 + 1, 4 ) ) as binary(4) ), 4194304,0xfd469501 )

        select @a = dbo.fn__md5_r(0, @a, @b, @c, @d, cast( reverse( substring( @buffer, 32 + 1, 4 ) ) as binary(4) ), 128,    0x698098d8 )
        select @d = dbo.fn__md5_r(0, @d, @a, @b, @c, cast( reverse( substring( @buffer, 36 + 1, 4 ) ) as binary(4) ), 4096,   0x8b44f7af )
        select @c = dbo.fn__md5_r(0, @c, @d, @a, @b, cast( reverse( substring( @buffer, 40 + 1, 4 ) ) as binary(4) ), 131072, 0xffff5bb1 )
        select @b = dbo.fn__md5_r(0, @b, @c, @d, @a, cast( reverse( substring( @buffer, 44 + 1, 4 ) ) as binary(4) ), 4194304,0x895cd7be )

        select @a = dbo.fn__md5_r(0, @a, @b, @c, @d, cast( reverse( substring( @buffer, 48 + 1, 4 ) ) as binary(4) ), 128,    0x6b901122 )
        select @d = dbo.fn__md5_r(0, @d, @a, @b, @c, cast( reverse( substring( @buffer, 52 + 1, 4 ) ) as binary(4) ), 4096,   0xfd987193 )
        select @c = dbo.fn__md5_r(0, @c, @d, @a, @b, cast( reverse( substring( @buffer, 56 + 1, 4 ) ) as binary(4) ), 131072, 0xa679438e )
        select @b = dbo.fn__md5_r(0, @b, @c, @d, @a, cast( reverse( substring( @buffer, 60 + 1, 4 ) ) as binary(4) ), 4194304,0x49b40821 )

        select @a = dbo.fn__md5_r(1, @a, @b, @c, @d, cast( reverse( substring( @buffer, 04 + 1, 4 ) ) as binary(4) ), 32,     0xf61e2562 )
        select @d = dbo.fn__md5_r(1, @d, @a, @b, @c, cast( reverse( substring( @buffer, 24 + 1, 4 ) ) as binary(4) ), 512,    0xc040b340 )
        select @c = dbo.fn__md5_r(1, @c, @d, @a, @b, cast( reverse( substring( @buffer, 44 + 1, 4 ) ) as binary(4) ), 16384,  0x265e5a51 )
        select @b = dbo.fn__md5_r(1, @b, @c, @d, @a, cast( reverse( substring( @buffer, 00 + 1, 4 ) ) as binary(4) ), 1048576,0xe9b6c7aa )

        select @a = dbo.fn__md5_r(1, @a, @b, @c, @d, cast( reverse( substring( @buffer, 20 + 1, 4 ) ) as binary(4) ), 32,     0xd62f105d )
        select @d = dbo.fn__md5_r(1, @d, @a, @b, @c, cast( reverse( substring( @buffer, 40 + 1, 4 ) ) as binary(4) ), 512,    0x2441453  )
        select @c = dbo.fn__md5_r(1, @c, @d, @a, @b, cast( reverse( substring( @buffer, 60 + 1, 4 ) ) as binary(4) ), 16384,  0xd8a1e681 )
        select @b = dbo.fn__md5_r(1, @b, @c, @d, @a, cast( reverse( substring( @buffer, 16 + 1, 4 ) ) as binary(4) ), 1048576,0xe7d3fbc8 )

        select @a = dbo.fn__md5_r(1, @a, @b, @c, @d, cast( reverse( substring( @buffer, 36 + 1, 4 ) ) as binary(4) ), 32,     0x21e1cde6 )
        select @d = dbo.fn__md5_r(1, @d, @a, @b, @c, cast( reverse( substring( @buffer, 56 + 1, 4 ) ) as binary(4) ), 512,    0xc33707d6 )
        select @c = dbo.fn__md5_r(1, @c, @d, @a, @b, cast( reverse( substring( @buffer, 12 + 1, 4 ) ) as binary(4) ), 16384,  0xf4d50d87 )
        select @b = dbo.fn__md5_r(1, @b, @c, @d, @a, cast( reverse( substring( @buffer, 32 + 1, 4 ) ) as binary(4) ), 1048576,0x455a14ed )

        select @a = dbo.fn__md5_r(1, @a, @b, @c, @d, cast( reverse( substring( @buffer, 52 + 1, 4 ) ) as binary(4) ), 32,     0xa9e3e905 )
        select @d = dbo.fn__md5_r(1, @d, @a, @b, @c, cast( reverse( substring( @buffer, 08 + 1, 4 ) ) as binary(4) ), 512,    0xfcefa3f8 )
        select @c = dbo.fn__md5_r(1, @c, @d, @a, @b, cast( reverse( substring( @buffer, 28 + 1, 4 ) ) as binary(4) ), 16384,  0x676f02d9 )
        select @b = dbo.fn__md5_r(1, @b, @c, @d, @a, cast( reverse( substring( @buffer, 48 + 1, 4 ) ) as binary(4) ), 1048576,0x8d2a4c8a )


        select @a = dbo.fn__md5_r(2, @a, @b, @c, @d, cast( reverse( substring( @buffer, 20 + 1, 4 ) ) as binary(4) ), 16,     0xfffa3942 )
        select @d = dbo.fn__md5_r(2, @d, @a, @b, @c, cast( reverse( substring( @buffer, 32 + 1, 4 ) ) as binary(4) ), 2048,   0x8771f681 )
        select @c = dbo.fn__md5_r(2, @c, @d, @a, @b, cast( reverse( substring( @buffer, 44 + 1, 4 ) ) as binary(4) ), 65536,  0x6d9d6122 )
        select @b = dbo.fn__md5_r(2, @b, @c, @d, @a, cast( reverse( substring( @buffer, 56 + 1, 4 ) ) as binary(4) ), 8388608,0xfde5380c )

        select @a = dbo.fn__md5_r(2, @a, @b, @c, @d, cast( reverse( substring( @buffer, 04 + 1, 4 ) ) as binary(4) ), 16,     0xa4beea44 )
        select @d = dbo.fn__md5_r(2, @d, @a, @b, @c, cast( reverse( substring( @buffer, 16 + 1, 4 ) ) as binary(4) ), 2048,   0x4bdecfa9 )
        select @c = dbo.fn__md5_r(2, @c, @d, @a, @b, cast( reverse( substring( @buffer, 28 + 1, 4 ) ) as binary(4) ), 65536,  0xf6bb4b60 )
        select @b = dbo.fn__md5_r(2, @b, @c, @d, @a, cast( reverse( substring( @buffer, 40 + 1, 4 ) ) as binary(4) ), 8388608,0xbebfbc70 )

        select @a = dbo.fn__md5_r(2, @a, @b, @c, @d, cast( reverse( substring( @buffer, 52 + 1, 4 ) ) as binary(4) ), 16,     0x289b7ec6 )
        select @d = dbo.fn__md5_r(2, @d, @a, @b, @c, cast( reverse( substring( @buffer, 00 + 1, 4 ) ) as binary(4) ), 2048,   0xeaa127fa )
        select @c = dbo.fn__md5_r(2, @c, @d, @a, @b, cast( reverse( substring( @buffer, 12 + 1, 4 ) ) as binary(4) ), 65536,  0xd4ef3085 )
        select @b = dbo.fn__md5_r(2, @b, @c, @d, @a, cast( reverse( substring( @buffer, 24 + 1, 4 ) ) as binary(4) ), 8388608,0x04881d05 )

        select @a = dbo.fn__md5_r(2, @a, @b, @c, @d, cast( reverse( substring( @buffer, 36 + 1, 4 ) ) as binary(4) ), 16,     0xd9d4d039 )
        select @d = dbo.fn__md5_r(2, @d, @a, @b, @c, cast( reverse( substring( @buffer, 48 + 1, 4 ) ) as binary(4) ), 2048,   0xe6db99e5 )
        select @c = dbo.fn__md5_r(2, @c, @d, @a, @b, cast( reverse( substring( @buffer, 60 + 1, 4 ) ) as binary(4) ), 65536,  0x1fa27cf8 )
        select @b = dbo.fn__md5_r(2, @b, @c, @d, @a, cast( reverse( substring( @buffer, 08 + 1, 4 ) ) as binary(4) ), 8388608,0xc4ac5665 )


        select @a = dbo.fn__md5_r(3, @a, @b, @c, @d, cast( reverse( substring( @buffer, 00 + 1, 4 ) ) as binary(4) ), 64,     0xf4292244 )
        select @d = dbo.fn__md5_r(3, @d, @a, @b, @c, cast( reverse( substring( @buffer, 28 + 1, 4 ) ) as binary(4) ), 1024,   0x432aff97 )
        select @c = dbo.fn__md5_r(3, @c, @d, @a, @b, cast( reverse( substring( @buffer, 56 + 1, 4 ) ) as binary(4) ), 32768,  0xab9423a7 )
        select @b = dbo.fn__md5_r(3, @b, @c, @d, @a, cast( reverse( substring( @buffer, 20 + 1, 4 ) ) as binary(4) ), 2097152,0xfc93a039 )

        select @a = dbo.fn__md5_r(3, @a, @b, @c, @d, cast( reverse( substring( @buffer, 48 + 1, 4 ) ) as binary(4) ), 64,     0x655b59c3 )
        select @d = dbo.fn__md5_r(3, @d, @a, @b, @c, cast( reverse( substring( @buffer, 12 + 1, 4 ) ) as binary(4) ), 1024,   0x8f0ccc92 )
        select @c = dbo.fn__md5_r(3, @c, @d, @a, @b, cast( reverse( substring( @buffer, 40 + 1, 4 ) ) as binary(4) ), 32768,  0xffeff47d )
        select @b = dbo.fn__md5_r(3, @b, @c, @d, @a, cast( reverse( substring( @buffer, 04 + 1, 4 ) ) as binary(4) ), 2097152,0x85845dd1 )

        select @a = dbo.fn__md5_r(3, @a, @b, @c, @d, cast( reverse( substring( @buffer, 32 + 1, 4 ) ) as binary(4) ), 64,     0x6fa87e4f )
        select @d = dbo.fn__md5_r(3, @d, @a, @b, @c, cast( reverse( substring( @buffer, 60 + 1, 4 ) ) as binary(4) ), 1024,   0xfe2ce6e0 )
        select @c = dbo.fn__md5_r(3, @c, @d, @a, @b, cast( reverse( substring( @buffer, 24 + 1, 4 ) ) as binary(4) ), 32768,  0xa3014314 )
        select @b = dbo.fn__md5_r(3, @b, @c, @d, @a, cast( reverse( substring( @buffer, 52 + 1, 4 ) ) as binary(4) ), 2097152,0x4e0811a1 )

        select @a = dbo.fn__md5_r(3, @a, @b, @c, @d, cast( reverse( substring( @buffer, 16 + 1, 4 ) ) as binary(4) ), 64,     0xf7537e82 )
        select @d = dbo.fn__md5_r(3, @d, @a, @b, @c, cast( reverse( substring( @buffer, 44 + 1, 4 ) ) as binary(4) ), 1024,   0xbd3af235 )
        select @c = dbo.fn__md5_r(3, @c, @d, @a, @b, cast( reverse( substring( @buffer, 08 + 1, 4 ) ) as binary(4) ), 32768,  0x2ad7d2bb )
        select @b = dbo.fn__md5_r(3, @b, @c, @d, @a, cast( reverse( substring( @buffer, 36 + 1, 4 ) ) as binary(4) ), 2097152,0xeb86d391 )

        -- exec sp__printf ''@c=%d, a=%d, b=%d, c=%d, d=%d'',@counter,@a,@b,@c,@d
        if @len=0 break

        end -- while counter

    end -- while 1=1

    close cs
    deallocate cs

    select @hash = cast( reverse( cast( ( @a + 0x67452301 ) as binary(4) ) )
                  + reverse( cast( ( @b + 0xefcdab89 ) as binary(4) ) )
                  + reverse( cast( ( @c + 0x98badcfe ) as binary(4) ) )
                  + reverse( cast( ( @d + 0x10325476 ) as binary(4) ) )
                  as binary(16) )


goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    calculate hash for table #src
    (for single strings, use fn__md5)

Parameters
    @skip   skin @skip (header)lines before init to calculate

Examples
    declare @hash binary(16)
    create table #src(lno int identity,line nvarchar(4000))
    insert #src(line) select ''''Test line 1''''
    insert #src(line) select ''''Test line 2''''
    insert #src(line) select ''''''''
    insert #src(line) select null
    exec sp__md5 @hash out      print @hash -- 0xC9AA7200A10304DD76F4C5B75F79C41C
    exec sp__md5 @hash out,1    print @hash -- 0x4F67A2ECFC0E84DB22585F13AAC9F8E6
    exec sp__md5 @hash out,2    print @hash -- 0xD4588FC69881818B29010299103452BF
    exec sp__md5 @hash out,3    print @hash -- 0xD41D8CD98F00B204E9800998ECF8427E
    drop table #src
''
ret:
return @ret
end -- sp__md5'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__md5:

-- =================================================================== sp__merge
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__merge',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130710
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__merge') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__merge') with nowait
        goto skip_sp__merge
        end
    if @ver>130710
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__merge') with nowait
        goto skip_sp__merge
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__merge') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__merge'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__merge]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130710\s.zaglio: added lkey for logic keys
    v:130211\s.zaglio: bug that causes a lost of last added fld(see code)
    v:120413\s.zaglio: add. exclusion of identity fld
    v:120113\s.zaglio: added code for correct compare
    v:110407\s.zaglio: added @no_ins,no_upd and code option
    v:110406\s.zaglio: added @excludes
    v:110322\s.zaglio: better help; added @where
    v:100928\s.zaglio: append mode, when @keys = @flds_tbl
    v:100730.1100\s.zaglio: more help
    v:100724\s.zaglio: added cmp option
    r:100718\s.zaglio: insert/update into one solution
    t:drop proc sp__merge
    t:sp__merge ''test_form'',''id''
*/
CREATE proc sp__merge
    @tbl        sysname =null,
    @flds_tbl   nvarchar(4000) =null,   -- or from
    @from       sysname =null,          -- or keys
    @flds_from  nvarchar(4000) =null,
    @keys       nvarchar(4000) =null,
    @flds_cmp   nvarchar(4000) =null,   -- upd fields
    @where      nvarchar(4000) =null,
    @no_ins     nvarchar(4000) =null,
    @no_upd     nvarchar(4000) =null,
    @error      int = null out,
    @inserted   bigint = null out,
    @updated    bigint = null out,
    @opt        sysname =null,
    @dbg        int=0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname,  @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=object_name(@@procid), @ret=0
select @opt=dbo.fn__str_quote(@opt,''|'')

-- declarations
declare @flds nvarchar(4000),@drop bit,@code bit
declare @excludes table(ins bit,obj sysname)
declare
    @op_cmp bit,@op_nochk bit,@op_moc bit,
    @op_121 bit,@op_nofmt bit,@op_lkey bit

select
    @op_cmp=charindex(''|cmp|'',@opt),
    @op_nochk=charindex(''|nochk|'',@opt),
    @op_moc=charindex(''|moc|'',@opt),
    @op_121=charindex(''|121|'',@opt),
    @op_nofmt=charindex(''|nofmt|'',@opt),
    @op_lkey=charindex(''|lkey|'',@opt)

-- simple: to,from,[key]
if not @tbl is null and not @flds_tbl is null and not @from is null
and @flds_from is null and @keys is null
    begin
    if @dbg>1 exec sp__printf ''simple syntax''
    select @keys=@from,@from=@flds_tbl,@flds_tbl=null
    end

if @tbl is null and @from is null goto help

if @op_nochk=0
and (dbo.fn__exists(@tbl,null)=0 or dbo.fn__exists(@from,null)=0)
    goto err_notf

-- drop table #fld
create table #fld(
    tid tinyint,        -- 1=fld,2=key,3=upd
    id int identity,
    tbl sysname      null,
    fld sysname,
    [from] sysname   null,
    from_fld sysname null,
    sep nvarchar(4)  null,
    cmm sysname      null,
    t_f              as rtrim(tbl)+rtrim(fld),
    f_f              as rtrim([from])+rtrim(from_fld),
    cmp_flds         as ''or (''+rtrim(tbl)+rtrim(fld)+''!=''+
                               rtrim([from])+rtrim(from_fld)+
                        '' or ((''+rtrim(tbl)+rtrim(fld)+'' is null or ''+
                                 rtrim([from])+rtrim(from_fld)+'' is null) ''+
                        ''and not coalesce(''+rtrim(tbl)+rtrim(fld)+'',''+
                                            rtrim([from])+rtrim(from_fld)+
                        '') is null))''
    )

declare @src table (lno int identity primary key,line nvarchar(4000))

declare
    @n int,@m int,@cr nchar(1),@lf nchar(1),@tab nchar(1),
    @sql nvarchar(4000),@id int

select
    @tbl=dbo.fn__sql_unquotename(@tbl),
    @from=dbo.fn__sql_unquotename(@from),
    @cr=cr,@lf=lf,@tab=tab,
    @no_ins=replace(@no_ins,''|'','',''),
    @no_upd=replace(@no_upd,''|'','',''),
    @code=0
from dbo.fn__sym()

insert @excludes(ins,obj) select 1,token from dbo.fn__str_table(@no_ins,'','')
insert @excludes(ins,obj) select 0,token from dbo.fn__str_table(@no_upd,'','')

/*
exec sp__merge
    ''tbl'',
    ''c1,    c2, c3,
     c4,c5'',
    ''from'',
    ''c1,c2,
     c3,c4'',
    ''c1,c2, c3 ,c4'',@dbg=1
*/

-- adjust programming format
select @n=4
while @n>0
    begin
    if @dbg>1 exec sp__printf ''adjusting flds of param %d'',@n

    if @n=4 select @flds=@flds_tbl
    if @n=3 select @flds=@flds_from
    if @n=2 select @flds=@keys
    if @n=1 select @flds=@flds_cmp

    select @flds=replace(@flds,@tab,'' '')
    select @flds=replace(@flds,@cr,'''')
    select @flds=replace(@flds,@lf,'''')
    while charindex(''  '',@flds)>0 select @flds=replace(@flds,''  '','' '')
    while charindex('', '',@flds)>0 select @flds=replace(@flds,'', '','','')
    while charindex('' ,'',@flds)>0 select @flds=replace(@flds,'' ,'','','')

    if @n=4 select @flds_tbl=@flds
    if @n=3 select @flds_from=@flds
    if @n=2 select @keys=@flds
    if @n=1 select @flds_cmp=@flds

    select @n=@n-1
    end -- while
/* ================================ body ================================== */

if @keys is null or @dbg>1
    begin
    exec sp__printf ''--tbl=%s\n--fld=%s\n--from=%s\n--ffld=%s\n--key=%s'',
                    @tbl,@flds_tbl,@from,@flds_from,@keys
    if @keys is null
        select
            @code=1,
            @keys=@flds_tbl,
            @from=@tbl,
            @flds_tbl=null
    end -- code mode

if @keys=''*'' select @keys=dbo.fn__flds_of(@tbl,'','',null)
if @flds_tbl is null
    select @flds_tbl=dbo.fn__flds_of(@tbl,'','',''%id%'')
if @flds_from is null
    select @flds_from =dbo.fn__flds_of(@from,'','',''%id%'')

select
    @n=dbo.fn__str_count(@flds_tbl,'',''),
    @m=dbo.fn__str_count(@flds_from,'','')
if @n!=@m and @op_moc=0 goto err_nmsf

declare @fsrc tinyint,@fdst tinyint,@fkey tinyint,@fcmp tinyint
select @fsrc=1,@fkey=2,@fcmp=3

-- to fields
insert into #fld(tid,fld)
select @fsrc,token
from dbo.fn__str_table(@flds_tbl,'','')
where not token in (select obj from @excludes)

-- from fields one to one
if @op_121=1
    update fld set from_fld=ff.token
    from #fld fld
    join dbo.fn__str_table(@flds_from,'','') ff
    on ff.pos=fld.id and @fsrc=fld.tid
else
    begin
    select @n=count(*) from #fld
    update fld set from_fld=ff.token
    from #fld fld
    join dbo.fn__str_table(@flds_from,'','') ff
    on ff.token=fld.fld and @fsrc=fld.tid
    if @@rowcount!=@n and @op_moc=0 goto err_nmsf
    end

-- remove not matched fields
delete from #fld where from_fld is null
update #fld set sep=case
                    when id=(select max(id) from #fld)
                    then '''' else '',''
                    end
where 1=#fld.tid

-- keys
select @n=dbo.fn__str_count(@keys,'','')
insert into #fld(tid,fld,from_fld,sep)
select
    @fkey,dbo.fn__str_at(token,''='',1),isnull(dbo.fn__str_at(token,''='',2),token),
    case when pos=1 then ''on '' else ''and '' end
from dbo.fn__str_table(@keys,'','')
join #fld fld on dbo.fn__str_at(token,''='',1)=fld.fld and @fsrc=fld.tid
order by pos
if @@rowcount!=@n goto err_nmkf

if @dbg>1 exec sp__select_astext ''
    select
        case tid
        when 1 then ''''S''''
        when 2 then ''''K''''
        when 3 then ''''C''''
        when 4 then ''''D''''
        end
        as [T],*
    from #fld
    where tid=2 order by id
''

-- extra update fields to compare
select @n=dbo.fn__str_count(@flds_cmp,'','')
insert into #fld(tid,fld,from_fld)
select @fcmp,fld.fld,from_fld
from #fld fld
join dbo.fn__str_table(@flds_cmp,'','')
on fld.fld=token and @fsrc=fld.tid
if @@rowcount!=@n goto err_nmuf

-- add table info and format fields
update #fld set
    tbl=''tbl.'',
    fld=quotename(fld),
    [from]= case when left(from_fld,1) like ''[0-9"(]''
            then '''' else ''tmp.''
            end,
    from_fld= case when left(from_fld,1) like ''[0-9"(]''
              then from_fld else quotename(from_fld)
              end

-- format
if @op_nofmt=0
    begin
    select @n=max(len(tbl+fld)),@m=max(len([from]+from_fld)) from #fld
    update #fld set
        fld=left(fld+replicate('' '',@n-len(tbl)),@n-len(tbl)),
        from_fld=left(from_fld+replicate('' '',@m-len([from])),@m-len([from])),
        cmm=dbo.fn__comment(quotename(@tbl)+''.''+quotename(fld))
    end

if @dbg>1 exec sp__select_astext ''
    select
        case tid
        when 1 then ''''S''''
        when 2 then ''''K''''
        when 3 then ''''C''''
        when 4 then ''''D''''
        end
        as [T],*
    from #fld
    order by id
''


if @op_cmp=1
    begin
    insert @src select    ''select''
    insert @src select    top 1
                        ''    case when ''+t_f+'' is null ''
                from    #fld
                where    tid=@fsrc
                order    by id

    insert @src select    ''    then ''''+'''' ''
    if @flds_cmp is null
        insert @src select    ''    else ''''''''''
    else
        begin
        insert @src select    ''    else case when 1!=1''
        insert @src select    ''         ''+cmp_flds
                    from    #fld
                    where    tid=@fcmp
                    order    by id
        insert @src select    ''         then ''''<'''' else ''''''''''
        insert @src select    ''         end''
        end -- flds_cmp
    insert @src select    ''    end as [*],''
    insert @src select    ''    ''+t_f+'',''+f_f+'' [<<]''+sep
                from    #fld
                where    tid=@fsrc
                order    by id
    insert @src select    ''from ''+quotename(@from)+'' tmp ''
    insert @src select    ''left join ''+quotename(@tbl)+'' tbl ''
    insert @src select    ''    ''+sep+''(''+t_f+''=''+f_f+
                          case when @op_lkey=1
                          then '' or (''+t_f+'' is null and ''+f_f+'' is null)''
                          else ''''
                          end+'')''
                from    #fld
                where    tid=2
    insert @src select ''where 1=1''
    if not @where is null insert @src select ''and ''+@where
    end     -- cmp
else
    begin   -- ins/upd
    -- generate update and insert script
    insert @src select ''declare @n bigint, @error int''
    -- title
    if not dbo.fn__comment(@tbl) is null
        insert @src select ''-- ''+@tbl+'': ''+dbo.fn__comment(@tbl)

    -- append/update
    if 0=(  select count(*)
            from #fld f
            left join #fld k on k.tid=@fkey and k.fld=f.fld
            where f.tid=@fsrc and k.id is null)
        begin
        if @dbg>1 exec sp__printf ''-- append mode''
        end
    else
        begin
        insert @src select ''''
        insert @src select ''update tbl set''
        insert @src select ''    ''+f.tbl+f.fld+''=''+
                           f.f_f+f.sep+
                           isnull('' -- ''+f.cmm,'''')
                    from #fld f
                    left join #fld k on k.tid=@fkey and k.fld=f.fld
                    where f.tid=@fsrc
                    -- and k.id is null 130211\s.zaglio
                    order by f.id
        insert @src select ''from ''+quotename(@tbl)+'' tbl ''
        insert @src select ''join ''+quotename(@from)+'' tmp ''
        insert @src select ''    ''+sep+''(''+t_f+''=''+f_f+
                           case when @op_lkey=1
                           then '' or (''+t_f+'' is null and ''+f_f+'' is null)''
                           else ''''
                           end+'')''
                    from #fld
                    where tid=2

        if not @flds_cmp is null
            begin
            insert @src select    ''where ''+
                                isnull(''(''+@where+'') and '','''')+
                                ''(1!=1''
            select top 1 @id=id from #fld where tid=3 order by id
            insert @src select    ''      ''+cmp_flds
            from    #fld
            where    tid=@fcmp
            order    by id
            insert @src select    ''      )''
            end

        insert @src select ''''
        insert @src select ''select @error=@@error,@n=@@rowcount''
        if @code=0
            insert @src select ''update #returns set error=@error,updated=@n''
        insert @src select ''if @error!=0 goto end_ins_upd''
        insert @src select ''''
        end -- update

    -- insert
    insert @src select ''insert into ''+quotename(@tbl)+''(''
    insert @src select ''    ''+fld+sep
                from #fld
                where tid=@fsrc
                order by id
    insert @src select ''    )''
    insert @src select ''select''
    insert @src select ''    ''+f_f+sep
                from #fld
                where tid=@fsrc
                order by id
    insert @src select ''from ''+quotename(@from)+'' tmp ''
    insert @src select ''left join ''+quotename(@tbl)+'' tbl ''
    insert @src select ''    ''+sep+''(''+t_f+''=''+f_f+
                       case when @op_lkey=1
                       then '' or (''+t_f+'' is null and ''+f_f+'' is null)''
                       else ''''
                       end+'')''
                from #fld
                where tid=@fkey
    insert @src select ''where 1=1''
    if @op_lkey=0
        insert @src select top 1        -- only one key fld is necessary
                        ''    and ''+t_f+'' is null''
                    from #fld
                    where tid=@fkey
                    order by id
    else
        -- when the "primary key" is "logic" ...
        insert @src select              -- ... all fields are necessary
                        ''    and ''+t_f+'' is null''
                    from #fld
                    where tid=@fkey
                    order by id

    insert @src select ''''
    insert @src select ''select @error=@@error,@n=@@rowcount''
    if @code=0
        insert @src select ''update #returns set error=@error,inserted=@n''

    insert @src select ''end_ins_upd:''
    insert @src select ''-- if @error!=0 rollback''

    end -- !cmp

if @code=0
    create table #src(lno int identity, line nvarchar(4000))

if @dbg>0
    begin
    select @n=max(lno) from #src
    select @n=isnull(@n,0)
    insert #src(line) select line from @src order by lno
    exec sp__print_table ''#src''
    delete from #src where lno>@n
    if @op_cmp=0
        exec sp__printframe ''### above code is not executed ###''
    end

if @code=0 and (@dbg=0 or @op_cmp!=0)
    begin
    create table #returns(error int,updated bigint,inserted bigint)
    insert #returns select 0,0,0
    insert #src(line) select line from @src order by lno
    exec @ret=sp__script_compile
    if @ret!=0 exec sp__print_table ''#src''
    select @error=error,@inserted=inserted,@updated=updated
    from #returns
    drop table #returns
    end

if @code=1
    begin
    if object_id(''tempdb..#src'') is null
        select line from @src order by lno
    else
        insert #src(line) select line from @src order by lno
    end

drop table #fld
if @code=0 drop table #src

goto ret

/* ================================ errors ================================ */

err_nmuf:   exec @ret=sp__err ''compare fields for update do not match with fields of tables'',@proc
            goto ret
err_nmkf:   exec @ret=sp__err ''key fields do not match with fields of tables'',@proc
            goto ret
err_nmsf:   exec @ret=sp__err ''selected fields do not match in both tables (see options 121,moc)'',@proc
            goto ret
err_notf:   exec @ret=sp__err ''source or dest table not found (see options nochk)'',@proc
            goto ret
err_whre:    exec @ret=sp__err ''@where param is not compatible with ...'',@proc
            goto ret
/* ================================ help ================================== */
help:
exec sp__usage @proc,''
Scope
    generate and run code for update and insert of records

Parameters
    Code generation syntax
        @tbl        as @tbl
        @flds_tbl   as @keys
        (NB: show generated code to cut and paste into I/O proc
        this is the better practice do not use directly sp__merge
        for I/O because there are many differences between two
        systems except for sync between two well normalized dbs)

    Simple syntax:
        @tbl        as @tbl
        @flds_tbl   as @from
        @from       as @keys

    Complete syntax:
        @tbl        to/destination table
        @flds_tbl   destination fields
        @from       from/source table
        @flds_from  source fields
        @keys       fields of keys for join (* for all of @tbl)
        @flds_cmp   fields for unic key for effective changes for update
        @where      condition applied to @tbl (uses tbl alias)
        @no_ins     exclude this fields (separated by comma) from insert list
                    see code option
        @no_upd     exclude this fields (separated by comma) from update list
                    see code option
        @error      return value of @error happened after insert or update
        @inserted   return number of rows inserted
        @updated    return number of rows update
        @opt        see below
        @dbg        if 1 show the generated code without execute it
                    ### this is useful to use sp__merge as code generatore ###
                    if 2 show debug info about inside tables

Options
    121     match 1st field of 1st table with 1st field of 2nd table and so on
    moc     match only common fields, that exist in both tables
    nochk   do not check existance of tables
    nofmt   dot not format
    lkey    set keys as logic and add compare of nulls
    cmp     show result of a compare select tbl.c1,tmp.c1 [<-]
    dbg     (todo) esecute ins/upd row by row with a cursor

Notes
    The append mode happen when the key is all fields. So only the insert
    statement is executed.

Examples

    -- operative table
    create table tst_base(id int, k1 sysname null, c datetime,act sysname)
    insert tst_base
    select row id,dbo.fn__format(row,"[eng]",default) k1,getdate(),"base"
    from fn__range(1,5,1) -- the last two line is unchanged

    -- temporary working table
    select id,k1,c,"delta" as act into #delta from tst_base
    delete from tst_base where id=1         -- this will added
    update #delta   set c=c+1  where id=2   -- this will updated
    update tst_base set c=null where id=3   -- this test update of nulls
    update tst_base set c=null where id=4   -- this test not update of nulls
    update #delta   set c=null where id=4   -- this test not update of nulls

    -- <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< direction
    -- test results using only keys
    exec sp__merge  "tst_base","#delta","id,k1",@opt="cmp",@dbg=1

    -- test results using keys and other fields
    exec sp__merge  "tst_base","#delta","id,k1",@flds_cmp="c",@opt="cmp",@dbg=1

    -- test code with extra flds comp
    exec sp__merge  @tbl="tst_base",
                    @from="#delta",
                    @keys="id,k1",
                    @flds_cmp="c,act"
                    ,@dbg=1

    -- append test mode
    exec sp__merge  "tst_base","#delta","*",@opt="cmp",@dbg=1

    -- code generation test
    exec sp__merge  "tst_base","id,k1",@flds_cmp="c"

    -- run it effectivelly (no dbg, no cmp) with where
    exec sp__merge  @tbl="tst_base",
                    @from="#delta",
                    @keys="id,k1",
                    @flds_cmp="c",
                    @where="tbl.id!=2"

    -- only 1st line is added and 3rd modified
    select * from tst_base

    drop table #delta
    drop table tst_base
''

select @ret=-1

/* ================================ exit ================================== */
ret:
return @ret

end -- proc sp__merge'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__merge:

-- =================================================================== sp__nulls
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__nulls',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110213
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__nulls') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__nulls') with nowait
        goto skip_sp__nulls
        end
    if @ver>110213
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__nulls') with nowait
        goto skip_sp__nulls
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__nulls') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__nulls'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__nulls]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110213\s.zaglio: nullize the parameters
    t:
        declare @v sysname,@i int,@r real,@d datetime
        select @v='''',@i=0,@d=0,@r=0.0
        exec sp__nulls @v out,@i out,@d out,@r out
        print isnull(@v,''(null)'')
        print isnull(@i,0)
        print isnull(@r,0)
        print isnull(@d,0)
*/
create proc sp__nulls
    @v0 nvarchar(4000) = null out,
    @v1 nvarchar(4000) = null out,
    @v2 nvarchar(4000) = null out,
    @v3 nvarchar(4000) = null out,
    @v4 nvarchar(4000) = null out,
    @v5 nvarchar(4000) = null out,
    @v6 nvarchar(4000) = null out,
    @v7 nvarchar(4000) = null out,
    @v8 nvarchar(4000) = null out,
    @v9 nvarchar(4000) = null out,
    @dbg int=0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)

-- ========================================================= param formal chk ==
exec @ret=sp__chknulls @v0,@v1,@v2,@v3,@v4,@v5,@v6,@v7,@v8,@v9
if @ret=1 goto help

-- ============================================================== declaration ==
declare @d0 sysname
-- =========================================================== initialization ==
-- ======================================================== second params chk ==

-- ===================================================================== body ==
select @d0=convert(sysname,convert(datetime,0))

if isdate(@v0)=1 select @v0=convert(sysname,convert(datetime,@v0))
if @v0 in ('''',''0'',@d0) select @v0=null

if isdate(@v1)=1 select @v1=convert(sysname,convert(datetime,@v1))
if @v1 in ('''',''0'',@d0) select @v1=null

if isdate(@v2)=1 select @v2=convert(sysname,convert(datetime,@v2))
if @v2 in ('''',''0'',@d0) select @v2=null

if isdate(@v3)=1 select @v3=convert(sysname,convert(datetime,@v3))
if @v3 in ('''',''0'',@d0) select @v3=null

if isdate(@v4)=1 select @v4=convert(sysname,convert(datetime,@v4))
if @v4 in ('''',''0'',@d0) select @v4=null

if isdate(@v5)=1 select @v5=convert(sysname,convert(datetime,@v5))
if @v5 in ('''',''0'',@d0) select @v5=null

if isdate(@v6)=1 select @v6=convert(sysname,convert(datetime,@v6))
if @v6 in ('''',''0'',@d0) select @v6=null

if isdate(@v7)=1 select @v7=convert(sysname,convert(datetime,@v7))
if @v7 in ('''',''0'',@d0) select @v7=null

if isdate(@v8)=1 select @v8=convert(sysname,convert(datetime,@v8))
if @v8 in ('''',''0'',@d0) select @v8=null

if isdate(@v9)=1 select @v9=convert(sysname,convert(datetime,@v9))
if @v9 in ('''',''0'',@d0) select @v9=null

goto ret

-- =================================================================== errors ==

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    nullize the parameters if:
    "" in strings,0 in numbers and dates

Examples
    declare @v sysname,@i int,@r real,@d datetime
    select @v='''',@i=0,@d=0,@r=0.0
    exec sp__nulls @v out,@i out,@d out,@r out
    print isnull(@v,''''(null)'''')
    print isnull(@i,0)
    print isnull(@r,0)
    print isnull(@d,0)
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__nulls'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__nulls:

-- ============================================================== sp__os_whereis
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__os_whereis',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140107.1700
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__os_whereis') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__os_whereis') with nowait
        goto skip_sp__os_whereis
        end
    if @ver>140107.1700
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__os_whereis') with nowait
        goto skip_sp__os_whereis
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__os_whereis') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__os_whereis'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__os_whereis]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:search,program,command,file,path,x86,any,system,64bit
    v:140107.1700\s.zaglio: search path of a program or command
    t:sp__os_whereis ''ftp.exe'',@dbg=1
    t:sp__os_whereis ''winscp\winscp.com'',@dbg=1
    t:sp__os_whereis ''winscp\nonscp.com'',@dbg=1
    t:sp__os_whereis ''windows nt\accessories\wordpad.exe'',@dbg=1
    t:xp_cmdshell ''C:\"Program Files"\"winscp"\"winscp.com"''
*/
CREATE proc sp__os_whereis
    @prg nvarchar(1024) = null out,
    @opt sysname = null,
    @dbg int = null
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0)                 -- is the verbosity level

-- ============================================================== declaration ==

declare
    @path nvarchar(1024),@env nvarchar(1024),@i int,
    @tmp nvarchar(1024),@cmd nvarchar(1024),@envs nvarchar(4000)

-- =========================================================== initialization ==
if @prg is null goto help

-- ======================================================== second params chk ==
if left(@prg,1)=''\'' raiserror(''path of program must be relative'',16,1)

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

-- xp_cmdshell ''set''
-- xp_cmdshell ''dir %SystemRoot%\system32\ftp.exe''
-- xp_cmdshell ''dir %SystemRoot%\wow*''
select
    @envs=''SystemRoot|ProgramFiles(x86)|ProgramFiles''

declare cs cursor local for
    select pos,token from dbo.fn__str_split(@envs,''|'')
open cs
while 1=1
    begin
    fetch next from cs into @i,@env
    if @@fetch_status!=0 break

    -- init env and path
    select @path=case @i
                 when 1 then ''System32\''
                 else ''''
                 end

    select @tmp=null
    exec sp__get_env @tmp out,@env
    if @tmp is null continue

    if right(@tmp,1)!=''\'' select @tmp=@tmp+''\''

    select @path=@tmp+@path+@prg
    select @cmd=''dir "''+@path+''"''
    exec @ret=xp_cmdshell @cmd,no_output
    if @ret=0
        begin
        -- convert path for dir into path for execute
        select @path=replace(replace(@path,''\'',''"\"''),'':"\"'','':\"'')+''"''
        break
        end

    end -- cursor cs
close cs
deallocate cs

select @prg=case @ret when 0 then @path else null end
if @dbg=1 exec sp__printf ''@prg:%s'',@prg

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    search into system and program paths the program and return the full path

Parameters
    [param]     [desc]
    @prg (out)  the name of command or of program to search
                out the full path
                NB: do not look into subdirectory, so the relative path
                    of the program must be specified (ex. winscp\winscp.com)
    @opt        not used
    @dbg        debug level
                1   basic info and do not execute dynamic sql
                2   more details (usually internal tables) and execute dsql
                3   basic info, execute dsql and show remote info

Examples
    @declare @prg nvarchar(4000)
    select @prg="ftp"
    exec sp__os_whereis @prg out
    if @prg is null raiserror(...)
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__os_whereis'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__os_whereis:

-- ============================================================ sp__outputbuffer
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__outputbuffer',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080923
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__outputbuffer') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__outputbuffer') with nowait
        goto skip_sp__outputbuffer
        end
    if @ver>080923
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__outputbuffer') with nowait
        goto skip_sp__outputbuffer
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__outputbuffer') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__outputbuffer'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__outputbuffer]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080923\S.Zaglio: used to get complete error description of @@error
    t:begin declare @s nvarchar(2000) exec(''truncate table_test'') exec sp__outputbuffer @s out print @s end
*/
CREATE   PROC sp__outputbuffer
    @errMessage     nvarchar(1000) out
AS
set nocount on
BEGIN
    DECLARE @dbccrow        nchar(77)
           ,@sql            nvarchar(2000)
           ,@hex            nchar(2)
           ,@byte           int
           ,@pos            int
           ,@numMsg         int
           ,@gather         int
           ,@count          int
           ,@byteNum        int
           ,@msgLen         int
           ,@errMsgLen      int
           ,@nchar          int
           ,@errNumber      bigint
           ,@errState       int
           ,@errLevel       int

           ,@errInstance    nvarchar(256)
           ,@errProcedure   nvarchar(256)
           ,@errLine        int

/*
A buffer sample

00000000   04 01 00 c8 00 37 01 00 aa 30 00 50 c3 00 00 01   ...È.7..ª0.PÃ...
00000010   10 07 00 54 00 45 00 53 00 54 00 20 00 30 00 31   ...T.E.S.T. .0.1
00000020   00 0a 45 00 4e 00 54 00 45 00 52 00 50 00 52 00   ..E.N.T.E.R.P.R.
00000030   49 00 53 00 45 00 00 01 00 00 00 fd 03 00 f6 00   I.S.E......ý..ö.
00000040   00 00 00 00 00 00 00 00 aa 30 00 50 c3 00 00 02   ........ª0.PÃ...
00000050   11 07 00 54 00 45 00 53 00 54 00 20 00 30 00 32   ...T.E.S.T. .0.2
00000060   00 0a 45 00 4e 00 54 00 45 00 52 00 50 00 52 00   ..E.N.T.E.R.P.R.
00000070   49 00 53 00 45 00 00 02 00 00 00 fd 03 00 f6 00   I.S.E......ý..ö.
00000080   00 00 00 00 00 00 00 00 aa 30 00 50 c3 00 00 03   ........ª0.PÃ...
00000090   12 07 00 54 00 45 00 53 00 54 00 20 00 30 00 33   ...T.E.S.T. .0.3
000000a0   00 0a 45 00 4e 00 54 00 45 00 52 00 50 00 52 00   ..E.N.T.E.R.P.R.
000000b0   49 00 53 00 45 00 00 03 00 00 00 fd 02 00 f6 00   I.S.E......ý..ö.
000000c0   00 00 00 00 00 00 00 00 30 00 20 00 36 00 64 00   ........0. .6.d.

We need to scan the buffer for the byte marker 0xAA that starts an error message.

The problem with this approach is that if the buffer contains any user data it might
have the marker byte and thus provoking a false response.

*/

    -- Catch the output buffer.
    CREATE TABLE #DBCCOUT (col1 nchar(77))
    INSERT INTO #DBCCOUT
         EXEC (''DBCC OUTPUTBUFFER(@@spid)'')

    CREATE TABLE #errors
    (
         errNumber    bigint
        ,errState     int
        ,errLevel     int
        ,errMessage   nvarchar(1000)
        ,errInstance  nvarchar(256)
        ,errProcedure nvarchar(256)
        ,errLine      int
    )

    -- Step through the buffer lines.
    DECLARE error_cursor CURSOR STATIC FORWARD_ONLY FOR
        SELECT col1
        FROM   #DBCCOUT
        ORDER  BY col1

    -- Init variable, and open cursor.
    OPEN error_cursor
    FETCH NEXT FROM error_cursor INTO @dbccrow

    -- Count the number of error messages
    SET @numMsg = 0

    SET @pos    = 12
    SET @gather = 0

    -- Now assemble rest of string.
    WHILE (@@FETCH_STATUS = 0)
    BEGIN
Start:
        IF (@pos > 57)
        BEGIN
            SET @pos = 12
            GOTO NextRow
        END

        -- Get a byte from th stream
        SET @hex = Substring(@dbccrow, @pos, 2)

        -- Convert hexstring to int
        SELECT @sql = ''SELECT @int = convert(int, 0x00'' + @hex + '')''
        EXEC sp_executesql @sql, N''@int int OUTPUT'', @byte output
        -- move to the next byte
        SET @pos = @pos + 3

        IF (@gather = 0)
        BEGIN
            /*
             * Searching for the 0xAA marker
             */
            IF (@byte != 170)
                GOTO Start

            SET @gather = 1
            SET @count  = 0
            SET @msgLen = 0
            GOTO Start
        END

        IF (@gather = 1)
        BEGIN
            /*
             * Get the Message Length
             */
            SET @count  = @count + 1
            SET @msgLen = (@msgLen * 256) + @byte

            IF (@count = 2)
            BEGIN
                SET @count     = 0
                SET @byteNum   = 0
                SET @errNumber = 0
                SET @gather    = 2
            END

            GOTO Start
        END

        /*
         * Count the number of bytes of the message
         */
        IF (@gather > 1)
            SET @byteNum = @byteNum + 1

        IF (@gather = 2)
        BEGIN
            /*
             * Get the error message
             */
            SET @errNumber = IsNull(@errNumber, 0) + (@byte * power(256, @count))
            SET @count     = @count + 1
            IF (@count = 4)
            BEGIN
                SET @count  = 0
                SET @gather = 3
            END

            GOTO Start
        END

        IF (@gather = 3)
        BEGIN
            /*
             * Get the Error State
             */
            SET @gather   = 4
            SET @errState = @byte

            GOTO Start
        END

        IF (@gather = 4)
        BEGIN
            /*
             * Get the Error Level
             */
            SET @gather   = 5
            SET @errLevel = @byte

            GOTO Start
        END

        IF (@gather = 5)
        BEGIN
            /*
             * Get the error message length
             */
            SET @errMsgLen = IsNull(@errMsgLen, 0) + (@byte * Power(256, @count))
            SET @count     = @count + 1
            IF (@count = 2)
            BEGIN
                SET @nchar  = 0
                SET @count  = 0
                SET @gather = 6
            END

            GOTO Start
        END

        IF (@gather = 6)
        BEGIN
            IF (@errMsgLen > 0)
            BEGIN
                /*
                 * Get the error message text
                 */
                SET @nchar = IsNull(@nchar, 0) + (@byte * Power(256, @count))
                SET @count = @count + 1
                IF (@count = 2)
                BEGIN
                    SET @count = 0
                    SET @errMessage = IsNull(@errMessage, '''') + nchar(@nchar)
                    SET @nchar = 0
                END

                IF (Len(@errMessage) = @errMsgLen)
                    SET @gather = 7

                GOTO Start
            END
            ELSE
                SET @gather = 7
        END

        IF (@gather = 7)
        BEGIN
            /*
             * Get the instance size
             */
            SELECT @gather    = 8
                  ,@errMsgLen = @byte
                  ,@nchar     = 0

            Goto Start
        END

        IF (@gather = 8)
        BEGIN
            IF (@errMsgLen > 0)
            BEGIN
                /*
                 * Get the instance name
                 */
                SET @nchar = IsNull(@nchar, 0) + (@byte * Power(256, @count))
                SET @count = @count + 1
                IF (@count = 2)
                BEGIN
                    SET @count = 0
                    SET @errInstance = IsNull(@errInstance, '''') + nchar(@nchar)
                    SET @nchar = 0
                END

                IF (Len(@errInstance) = @errMsgLen)
                    SET @gather = 9

                GOTO Start
            END
            ELSE
                SET @gather = 9
        END

        IF (@gather = 9)
        BEGIN
            /*
             * Get the procedure size
             */
            SELECT @gather    = 10
                  ,@errMsgLen = @byte
                  ,@nchar     = 0

            Goto Start
        END

        IF (@gather = 10)
        BEGIN
            IF (@errMsgLen > 0)
            BEGIN
                /*
                 * Get the procedure name
                 */
                SET @nchar = IsNull(@nchar, 0) + (@byte * Power(256, @count))
                SET @count = @count + 1
                IF (@count = 2)
                BEGIN
                    SET @count = 0
                    SET @errProcedure = IsNull(@errProcedure, '''') + nchar(@nchar)
                    SET @nchar = 0
                END

                IF (Len(@errProcedure) = @errMsgLen)
                    SET @gather = 11

                GOTO Start
            END
            ELSE
                SET @gather = 11
        END

        IF (@gather = 11)
        BEGIN
            /*
             * Get the error message length
             */
            SET @errLine = IsNull(@errLine, 0) + (@byte * Power(256, @count))
            SET @count   = @count + 1
            IF (@count = 2)
            BEGIN
                SET @nchar  = 0
                SET @count  = 0
                SET @gather = 0
                SET @nchar  = 0

                INSERT #errors VALUES (@errNumber, @errState, @errLevel, @errMessage, @errInstance, @errProcedure, @errLine)
            END

            GOTO Start
        END

NextRow:
        FETCH NEXT FROM error_cursor INTO @dbccrow
    END

    CLOSE error_cursor
    DEALLOCATE error_cursor

    SELECT top 1 @errMessage=errMessage FROM #errors
END'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__outputbuffer:

-- =========================================================== sp__outputbuffer1
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__outputbuffer1',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080923
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__outputbuffer1') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__outputbuffer1') with nowait
        goto skip_sp__outputbuffer1
        end
    if @ver>080923
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__outputbuffer1') with nowait
        goto skip_sp__outputbuffer1
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__outputbuffer1') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__outputbuffer1'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__outputbuffer1]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080923\S.Zaglio: used to get complete error description of @@error
    t:begin declare @s nvarchar(2000) exec(''truncate table_test'') exec sp__outputbuffer @s out print @s end
*/
CREATE PROCEDURE sp__outputbuffer1 @errmsg nvarchar(500) OUTPUT AS
    DECLARE @dbccrow nchar(77),
            @msglen  int,
            @lenstr  nchar(2),
            @sql     nvarchar(2000),
            @s       tinyint

    -- Catch the output buffer.
    CREATE TABLE #DBCCOUT (col1 nchar(77) NOT NULL)
    INSERT INTO #DBCCOUT
         EXEC (''DBCC OUTPUTBUFFER(@@spid)'')

    -- Set up a cursor over the table. We skip the first
    -- row, because there is nothing of interest.
    DECLARE error_cursor CURSOR STATIC FORWARD_ONLY FOR
        SELECT col1
        FROM   #DBCCOUT
        WHERE  left(col1, 8) <> replicate(''0'', 8)
        ORDER  BY col1

    -- Init variable, and open cursor.
    SELECT @errmsg  = ''''
    OPEN error_cursor
    FETCH NEXT FROM error_cursor INTO @dbccrow

    -- On this first row we find the length.
    SELECT @lenstr = substring(@dbccrow, 15, 2)

    -- Convert hexstring to int
    SELECT @sql = ''SELECT @int = convert(int, 0x00'' + @lenstr + '')''
    EXEC sp_executesql @sql, N''@int int OUTPUT'', @msglen OUTPUT

    -- @s is where the ntext part of the buffer starts.
    SELECT @s = 62

    -- Now assemble rest of string.
    WHILE @@FETCH_STATUS = 0 AND datalength(@errmsg) - 1 < 2 * @msglen
    BEGIN
      SELECT @errmsg = @errmsg + substring(@dbccrow, @s + 1, 1) +
                                 substring(@dbccrow, @s + 3, 1) +
                                 substring(@dbccrow, @s + 5, 1) +
                                 substring(@dbccrow, @s + 7, 1) +
                                 substring(@dbccrow, @s + 9, 1) +
                                 substring(@dbccrow, @s + 11, 1) +
                                 substring(@dbccrow, @s + 13, 1) +
                                 substring(@dbccrow, @s + 15, 1)
      FETCH NEXT FROM error_cursor INTO @dbccrow
    END

    CLOSE error_cursor
    DEALLOCATE error_cursor

    -- Now chop first character which is the length, and cut after end.
    SELECT @errmsg = substring(@errmsg, 2, @msglen)'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__outputbuffer1:

-- ================================================================== sp__paging
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__paging',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091012
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__paging') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__paging') with nowait
        goto skip_sp__paging
        end
    if @ver>091012
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__paging') with nowait
        goto skip_sp__paging
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__paging') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__paging'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__paging]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091012\s.zaglio: show a table paged as in web
    c:from http://snipplr.com/view/112/sqlserver-2000-tsql-stored-procedure-for-providing-paginated-results/
    t:sp__paging @tables=''[sysobjects]'',@pk=''[sysobjects].id'',@sort=''id'',@pagenumber=1,@pagesize=5
*/
CREATE PROCEDURE sp__paging
(
    @TABLES nvarchar(1000),
    @PK nvarchar(100),
    @JoinStatements nvarchar(1000)='''',
    @FIELDS nvarchar(4000) = ''*'',
    @Filter nvarchar(4000) = ''1=1'',
    @Sort nvarchar(200) = NULL,
    @PageNumber int = 1,
    @PageSize int = 10,
    @TotalRec int =0 Output,
    @GROUP nvarchar(1000) = NULL
)
AS
/*
Created by Kashif Akram
Email Muhammad_kashif@msn.com
The publication rights are reserved
You can use this procedure with out removing these comments
*/
DECLARE @strPageSize nvarchar(50)
DECLARE @strStartRow nvarchar(50)
SET @strPageSize = CAST(@PageSize AS nvarchar(50))
SET @strStartRow = CAST(((@PageNumber - 1)*@PageSize + 1) AS nvarchar(50))
--set @PK ='' tbl_Items.ItemID ''
CREATE TABLE #PageTable (PID bigint primary key IDENTITY (1, 1) , UID int)
CREATE TABLE #PageIndex (UID int)
/*
CREATE UNIQUE CLUSTERED
INDEX [PK_tbl_PageTable] ON #PageTable (PID)
*/
CREATE
INDEX [PK_tbl_PageIndex] ON #PageIndex (UID)
--''SELECT '' + @Fields + '' FROM '' + @Tables + '''' + @JoinStatements +'' WHERE '' + @strSortColumn + @operator + '' @SortColumn '' + @strSimpleFilter + '' '' + @strGroup + '' ORDER BY '' + @Sort + '' DESC ''
exec (''
set rowcount 0
insert into #pageTable(UID)
SELECT '' + @PK + '' FROM '' + @TABLES + '' '' + @JoinStatements +'' WHERE '' + @Filter + '' '' + @GROUP + '' ORDER BY '' + @Sort + ''
DECLARE @SortColumn int
SET ROWCOUNT ''+ @strStartRow +''
select @SortColumn=PID from #PageTable --option (keep plan)
print @SortColumn
SET ROWCOUNT ''+ @strPageSize +''
insert into #pageIndex
select UID from #PageTable where PID >= @SortColumn -- option (keep plan)
SELECT '' + @FIELDS + '' FROM '' + @TABLES + '' '' + @JoinStatements +'' WHERE '' + @Filter + '' and ''+ @PK + '' in (Select UID from #pageIndex)'' + @GROUP + '' ORDER BY '' + @Sort + '' ''
)
SELECT @TotalRec=count(*) FROM #pageTable
DROP TABLE #PageTable
DROP TABLE #PageIndex
RETURN'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__paging:

-- ============================================================== sp__parse_date
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__parse_date',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110213
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__parse_date') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__parse_date') with nowait
        goto skip_sp__parse_date
        end
    if @ver>110213
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__parse_date') with nowait
        goto skip_sp__parse_date
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__parse_date') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__parse_date'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__parse_date]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    todo: finish all tests and add more
    r:110213\s.zaglio: parse a formatted string date
    t:
        sp__parse_date 13,@dbg=1
        sp__parse_date ''13 20.30'',@dbg=1
        sp__parse_date -1,@dbg=1
        sp__parse_date ''-1 00.00'',@dbg=1
        sp__parse_date ''-1 0'',@dbg=1        -- todo: yersterday at midnight
        sp__parse_date ''-1 0.0'',@dbg=1        -- todo: yersterday at midnight
        sp__parse_date ''-1 -1'',@dbg=1        -- todo: yersterday one month ago
*/
create proc sp__parse_date
    @dates      nvarchar(64) = null,
    @date       datetime = null out,
    @lng        sysname = null,
    @dbg        int=0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)

-- ========================================================= param formal chk ==
if @dates is null goto help

-- ============================================================== declaration ==
declare
    @n int,@day int,@tk1 sysname,@tk2 sysname,@tk3 sysname,@tk4 sysname

-- =========================================================== initialization ==

-- ======================================================== second params chk ==

-- ===================================================================== body ==
if isnull(@lng,'''')!=''''
    begin
    set language @lng
    if @@error!=0 select @ret=-1
    if @ret!=0 goto ret
    end

if isdate(@dates)=1 begin select @date=convert(datetime,@dates) goto ret end
if isnumeric(@dates)=1
    begin
    select @day=convert(int,@dates)
    if @day>0
        begin
        select @date=getdate()
        select @date=@date-day(@date)+@day
        end
    else
        select @date=getdate()+@day
    goto dispose
    end

select @n=dbo.fn__str_count(@dates,'''')
select @tk1=dbo.fn__str_at(@dates,'''',1)
select @tk2=dbo.fn__str_at(@dates,'''',2)
select @tk3=dbo.fn__str_at(@dates,'''',3)
select @tk4=dbo.fn__str_at(@dates,'''',4)
if isnumeric(@tk1)=1 and (charindex(''.'',@tk2)>0 or charindex('':'',@tk2)>0)
    begin
    select @day=convert(int,@tk1)
    if @day>0
        begin
        select @date=getdate()
        select @date=@date-day(@date)+@day
        end
    else
        select @date=getdate()+@day
    select @dates=convert(sysname,@date,126)
    select @dates=substring(@dates,1,charindex(''T'',@dates))
    -- iso(126): yyyy-mm-ddThh:mm:ss.mmm
    select @tk2=replace(@tk2,''.'','':'')
    select @n=dbo.fn__str_count(@dates,'':'')
    if @n=1 select @tk2=@tk2+'':00.000''
    select @dates=@dates+@tk2
    select @date=convert(datetime,@dates,126)
    goto dispose
    end -- day hour

dispose:
if @dbg=1 exec sp__printf ''dates=%s, date=%s, tk1=%s tk2=%s'',@dates,@date,@tk1,@tk2

goto ret

-- =================================================================== errors ==

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    parse a formatted string date

Parameters
    @dates  date in string(input) format
    @date   the converted date
    @lng    language (default current; see set language or sys.syslanguages)
    return  a non zero value if an error

Examples
    value           convertion
    "full iso"      considered
    13              considered current day
    13 20.30        day 13 of current month/year, hour 20.30
    13 20:30        day 13 of current month/year, hour 20.30
    -1              today less one day
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__parse_date'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__parse_date:

-- ========================================================== sp__parse_url_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__parse_url_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130906.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__parse_url_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__parse_url_test') with nowait
        goto skip_sp__parse_url_test
        end
    if @ver>130906.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__parse_url_test') with nowait
        goto skip_sp__parse_url_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__parse_url_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__parse_url_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__parse_url_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:url,uri,parse,path,web,internet
    v:130906.1000\s.zaglio: test and show use of fn__parse_url
    t:sp__parse_url_test
*/
CREATE proc sp__parse_url_test
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare @urls table(url sysname)
declare @url sysname
-- =========================================================== initialization ==
insert @urls
-- select ''protocol://<username:password@>nomehost<:port></path><?querystring>'' union
select ''svn://lupin/SOURCES\*.vb'' union
select ''file:///C:/Documents%20and%20Settings/ste...'' union
select ''file://///svr/share/dir/sdir/file.ext'' union
select ''file://svr/share/dir/sdir/file.ext'' union
select ''https://sap.mymeetingroom.com/?content...'' union
select ''http://maps.google.com/maps/api/geocode/xml?sensor=false&address=8+soratino,+italy'' union
select ''https//sap.mymeetingroom.com/?content...'' union
select ''ftp://pwd:usr@site.org'' union
select ''\\svr\share\dir\file.ext'' union
select ''\\pwd:uid@svr\share\dir\file.ext'' union
select ''c:\share\dir\file.ext''

-- ======================================================== second params chk ==
-- =============================================================== #tbls init ==

select top 0 cast(null as sysname) status,*
into #tmp
from dbo.fn__parse_url('''',default)

-- ===================================================================== body ==

declare cs cursor local for select url from @urls
open cs
while 1=1
    begin
    fetch next from cs into @url
    if @@fetch_status!=0 break
    begin try
    insert #tmp select ''ok'',* from fn__parse_url(@url,default)
    end try
    begin catch
    insert #tmp(status,url) select ''ko:''+error_message(),@url
    end catch
    end -- cursor cs
close cs
deallocate cs

update #tmp set status=''ko'' where normalized is null

select * from #tmp order by status
-- ================================================================== dispose ==
dispose:
drop table #tmp

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test fn__parse_url

Parameters
    [param]     [desc]
    @opt        options (not used)
    @dbg        not used
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__parseurl_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__parse_url_test:

-- ==================================================================== sp__perf
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__perf',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130707.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__perf') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__perf') with nowait
        goto skip_sp__perf
        end
    if @ver>130707.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__perf') with nowait
        goto skip_sp__perf
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__perf') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__perf'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__perf]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130707.1000\s.zaglio: added info about wait events
    v:130222\s.zaglio: added perf code in comments for future implementation
    v:120811\s.zaglio: added comment about WMI
    v:120220.1748\s.zaglio: added io perf
    v:120104\s.zaglio: re-enabled record
    v:111011\s.zaglio: a remake
    v:111005\s.zaglio: added @waitfor
    v:110926\s.zaglio: refine
    d:110926\s.zaglio: sp__perf_top25
    v:100129\s.zaglio: print top 25 heavy processes
    t:sp__perf_old
    t:sp__perf @opt=''record'' select * from tmp_stat_perf order by readed desc
    t:sp_monitor
*/
CREATE proc sp__perf
    @waitfor sysname=null,
    @opt sysname=null
as
begin
set nocount on
declare @proc sysname,@ret int,@err int
select
    @err=0,@ret=0,@proc=object_name(@@procid),
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if not @waitfor is null goto err_prm
/*
130221\s.zaglio: query useful to detect perf. problem
-- select wait_type,count(*) n from sys.dm_exec_requests group by wait_type order by count(*) desc
-- select * from sys.dm_exec_requests
-- SELECT COUNT(*) AS ''Number of waiting tasks'' FROM sys.dm_os_waiting_tasks;
-- SELECT COUNT(*) AS ''Number of threads'' FROM sys.dm_os_waiting_tasks WHERE wait_type <> ''THREADPOOL'';
-- SELECT CAST(wait_type AS VARCHAR(30)) AS ''Waiting task'', COUNT (*) AS ''Number of waiting tasks'' FROM sys.dm_os_waiting_tasks GROUP BY wait_type ORDER BY ''Number of waiting tasks'' DESC;
-- EXEC sys.sp_configure N''max degree of parallelism''
-- select * from sys.dm_os_nodes -- mssql2k12
-- SELECT waiting_task_address AS ''Task address'',session_id AS ''Session'',exec_context_id AS ''Context'',wait_duration_ms AS ''Wait in millsec'',CAST(wait_type AS VARCHAR(30)) AS ''Type'',resource_address AS ''Resource address'',blocking_task_address AS ''Blocking task'',blocking_session_id AS ''Blocking session'',CAST(resource_description AS VARCHAR(30)) AS ''Resource'' FROM sys.dm_os_waiting_tasks WHERE wait_duration_ms > 20 AND wait_type LIKE ''%PAGEIOLATCH%'';
-- sp__perf_monitor
-- SELECT resource_address AS ''Resource Bottleneck'',COUNT (*) AS ''# of bottlenecks'' FROM sys.dm_os_waiting_tasks WHERE resource_address <> 0 GROUP BY resource_address ORDER BY ''# of bottlenecks'' DESC;
-- select ''UPDATE STATISTICS ''+name+'' WITH fullscan'' from sysobjects where xtype=''U''
-- http://blogs.msdn.com/b/cindygross/archive/2011/01/28/the-ins-and-outs-of-maxdop.aspx
-- the phisical cpu count is wrong on xeon that has 1 processor/package 4 cpus and 4 threads but 1 x cpu
-- select cpu_count as [logical cpus], hyperthread_ratio as hyperthread_ratio,cpu_count/hyperthread_ratio as physical_cpu_count,physical_memory_in_bytes/1048576 as physical_memory_in_mb from sys.dm_os_sys_info

-- if is mssql2k8
-- https://www.simple-talk.com/sql/performance/investigating-sql-server-2008-wait-events-with-xevents/

-- ms resource about perf problems
-- http://technet.microsoft.com/en-us/library/cc966540.aspx

-- simple and correct cpu use calc, tested and compared with visual graphics
declare @cpu_busy int, @idle int, @io_busy int
select @cpu_busy = @@cpu_busy, @idle = @@idle , @io_busy=@@io_busy
waitfor delay ''000:00:01''
select
    (@@cpu_busy - @cpu_busy)/((@@idle - @idle + @@cpu_busy - @cpu_busy) *1.00) *100 as ''cpu'',
    (@@io_busy - @io_busy)/((@@idle - @idle + @@io_busy - @io_busy) *1.00) *100 as ''io''

-- Total waits are wait_time_ms (high signal waits indicates CPU pressure)
-- if pct signal wait > 10%, means that more cpu are required
SELECT CAST(100.0 * SUM(signal_wait_time_ms) / SUM(wait_time_ms) AS NUMERIC(20,2)) AS [%signal (cpu) waits] , CAST(100.0 * SUM(wait_time_ms - signal_wait_time_ms) / SUM(wait_time_ms) AS NUMERIC(20, 2)) AS [%resource waits]FROM sys.dm_os_wait_stats ;

-- Isolate top waits for server instance since last restart
-- or statistics clear
WITH Waits AS ( SELECT wait_type , wait_time_ms / 1000. AS wait_time_s , 100. * wait_time_ms / SUM(wait_time_ms) OVER ( ) AS pct ,
ROW_NUMBER() OVER ( ORDER BY wait_time_ms DESC ) AS rn FROM sys.dm_os_wait_stats WHERE wait_type NOT IN ( ''CLR_SEMAPHORE'', ''LAZYWRITER_SLEEP'', ''RESOURCE_QUEUE'', ''SLEEP_TASK'', ''SLEEP_SYSTEMTASK'', ''SQLTRACE_BUFFER_FLUSH'', ''WAITFOR'', ''LOGMGR_QUEUE'', ''CHECKPOINT_QUEUE'', ''REQUEST_FOR_DEADLOCK_SEARCH'', ''XE_TIMER_EVENT'', ''BROKER_TO_FLUSH'', ''BROKER_TASK_STOP'', ''CLR_MANUAL_EVENT'', ''CLR_AUTO_EVENT'', ''DISPATCHER_QUEUE_SEMAPHORE'', ''FT_IFTS_SCHEDULER_IDLE_WAIT'', ''XE_DISPATCHER_WAIT'', ''XE_DISPATCHER_JOIN'' ) ) SELECT W1.wait_type , CAST(W1.wait_time_s AS DECIMAL(12, 2)) AS wait_time_s , CAST(W1.pct AS DECIMAL(12, 2)) AS pct , CAST(SUM(W2.pct) AS DECIMAL(12, 2)) AS running_pct FROM Waits AS W1 INNER JOIN Waits AS W2 ON W2.rn <= W1.rn GROUP BY W1.rn , W1.wait_type , W1.wait_time_s , W1.pct HAVING SUM(W2.pct) - W1.pct < 95 ; -- percentage threshold

-- Recovery model, log reuse wait description, log file size,
-- log usage size and compatibility level for all databases on instance
SELECT db.[name] AS [Database Name] , db.recovery_model_desc AS [Recovery Model] , db.log_reuse_wait_desc AS [Log Reuse Wait Description] , ls.cntr_value AS [Log Size (KB)] ,
lu.cntr_value AS [Log Used (KB)] , CAST(CAST(lu.cntr_value AS FLOAT) / CAST(ls.cntr_value AS FLOAT) AS DECIMAL(18,2)) * 100 AS [Log Used %] , db.[compatibility_level] AS [DB Compatibility Level] , db.page_verify_option_desc AS [Page Verify Option]
FROM sys.databases AS db
INNER JOIN sys.dm_os_performance_counters AS lu
    ON db.name = lu.instance_name
INNER JOIN sys.dm_os_performance_counters AS ls
    ON db.name = ls.instance_name
WHERE lu.counter_name LIKE ''Log File(s) Used Size (KB)%'' AND ls.counter_name LIKE ''Log File(s) Size (KB)%'' ;

---- average stalls per read, write and total
---- adding 1.0 to avoid division by zero errors
select db_name(database_id) db, file_id
    ,io_stall_read_ms
    ,num_of_reads
    ,cast(io_stall_read_ms/(1.0+num_of_reads) as numeric(10,1)) as ''avg_read_stall_ms''
    ,io_stall_write_ms
    ,num_of_writes
    ,cast(io_stall_write_ms/(1.0+num_of_writes) as numeric(10,1)) as ''avg_write_stall_ms''
    ,io_stall_read_ms + io_stall_write_ms as io_stalls
    ,num_of_reads + num_of_writes as total_io
    ,cast((io_stall_read_ms+io_stall_write_ms)/(1.0+num_of_reads + num_of_writes) as numeric(10,1)) as ''avg_io_stall_ms''
from sys.dm_io_virtual_file_stats(null,null)
order by avg_io_stall_ms desc

-- transactions x second
declare @n int
select @n=cntr_value
from sys.dm_os_performance_counters
where counter_name = ''transactions/sec''
and rtrim(object_name) like ''%:databases''
and instance_name = db_name()
waitfor delay ''00:00:01''
select (cntr_value-@n)/1
from sys.dm_os_performance_counters
where counter_name = ''transactions/sec''
and rtrim(object_name) like ''%:databases''
and instance_name = db_name()

*/

/*

    DBCC SQLPERF (Waitstats)
        PAGELATCH_EX indicated waits for physical I/O
        CXPACKET indicates waits for parallel processes to complete.
        NETWORKIO indicates waits for Network I/O

        For NETWORKIO, use perfmon to determine the workload and if there is sufficient thruput available.
        For CXPACKET, if this is primarily OLTP transactions, suggest disabling parallelism (max degree of 1 ).
        For PAGELATCH_EX, suggest you gather database file I/O statistics on a regular basis to determine if there is a disk resource problem:

    select db_name(dbid) db,IoStallMS / ( NumberReads + NumberWrites ) as MsPerIo,*
    from :: fn_virtualfilestats(default,default)
    order by IoStallMS / ( NumberReads + NumberWrites ) desc

        The statistics are since the SQL Server was started,
        so you will need to determine the changes between two sets of statistics yourself.
        MsPerIo should be under 8 and under 4 is desirable. Also use perfmon to check the disk queue length.

        I second Grant Fritchey''s recommendation to run a trace.
        There are MS provided tools to analyze the traces that are not very friendly (obtuse command line switches )
        but the results are incredibly useful and almost impossible to produce manually.
        http://support.microsoft.com/kb/944837

    Consider this http://thomaslarock.com/2011/01/wmi-code-creator/ about WMI
    but it does not work in production and return an absurd value.

*/
-- 5/6/2003 sp - sqlhogs 2.0
-- modify below to proper number of processors
--   (hyperthreaded and multi-core count so probably just see how many show in task mgr)
-- 2/2/07 sp - modified to sum for ecids per spid, as opposed to just showing ecit=0
-- grabs two snapshots from sysperfinfo and presents a delta between them
-- to estimate sql server usage.
declare
    @icpucount int,
    @cpu_busy bigint, @idle_busy bigint,@io_busy bigint,
    @cpu numeric(32,2), @idle numeric(32,2),@io numeric(32,2),
    @secfromstart numeric (18,2),
    @dt datetime,@s int,
    @record bit

select
    @record=charindex(''|record|'',@opt),
    @dt=getdate(),
    @secfromstart =
        datediff(s, (
            select top 1 login_time  -- start time of the instance
            from master..sysprocesses
            where cmd=''lazy writer''
            ),
            getdate()
        )

exec(''
create proc #iostats_fill
as
insert #iostats
select *
from (
    select
        getdate() as capture_time,
        --virtual file latency
        readlatency = case when num_of_reads = 0
            then 0 else (io_stall_read_ms / num_of_reads) end,
        writelatency = case when num_of_writes = 0
            then 0 else (io_stall_write_ms / num_of_writes) end,
        latency = case when (num_of_reads = 0 and num_of_writes = 0)
            then 0 else (io_stall / (num_of_reads + num_of_writes)) end,
        --avg bytes per iop
        avgbperread = case when num_of_reads = 0
            then 0 else (num_of_bytes_read / num_of_reads) end,
        avgbperwrite = case when io_stall_write_ms = 0
            then 0 else (num_of_bytes_written / num_of_writes) end,
        avgbpertransfer = case when (num_of_reads = 0 and num_of_writes = 0)
            then 0 else ((num_of_bytes_read + num_of_bytes_written) /
                (num_of_reads + num_of_writes)) end,
        left (mf.physical_name, 2) as drive,
        db_name (vfs.database_id) as db,
        --vfs.*,
        substring(
            mf.physical_name,
            len(mf.physical_name)-
                charindex(''''\'''',reverse(mf.physical_name))+2, 100
            )
        as file_name,
        num_of_writes+num_of_reads nwr
    -- select *
    from sys.dm_io_virtual_file_stats (null,null) as vfs
    join sys.master_files as mf
        on vfs.database_id = mf.database_id
        and vfs.file_id = mf.file_id
    ) vfs
where 1=1
    and latency>50
-- vfs.file_id = 2 -- log files
-- order by latency desc
-- order by readlatency desc
-- order by writelatency desc;
'')
-- drop table #iostats
create table #iostats (
    capture_time    datetime,
    readlatency     bigint null,
    writelatency    bigint null,
    latency         bigint null,
    avgbperread     bigint null,
    avgbperwrite    bigint null,
    avgbpertransfer bigint null,
    drive           nvarchar(2)  null,
    db              nvarchar(128)  null,
    file_name       nvarchar(100)  null,
    nwr             bigint null
    )

-- get number of processors
/*
select  @icpucount=cpu_count / hyperthread_ratio as physical_cpu_sockets
from    sys.dm_os_sys_info ;
*/
create table #numprocs
(
        id int,
        colname varchar(128),
        iv int,
        cv varchar(128)
)

exec(''exec #iostats_fill'')

select @waitfor=''%''+@waitfor+''%''
while (1=1)
    begin

    insert #numprocs
            exec master..xp_msver
    select @icpucount = iv from #numprocs
            where colname like ''%processorcount%''
    drop table #numprocs

    -- dbcc inputbuffer(spid) - shows the command that was run
    -- spid = sql server process id.
    -- kpid = windows thread id.  the thread id shows the kpid for a given sql server thread.
    -- blocked = spid of the blocking process.
    -- waittime = ms.
    -- dbid = database id
    -- uid = user id
    -- cpu = ms
    -- physical io = physical reads and writes
    -- memusage = number of pages in proc cache for this spid
    -- last_batch = time of last exec or stored proc
    -- ecid = identifies subthreads within a spid

    -- get the two snapshots, 1 second apart.
    -- exclude user = system.
    select 1 as snapnum, spid, kpid, blocked,
        waittime, dbid, uid, cpu,
        physical_io, memusage, login_time, last_batch,
        ecid, status, hostname, program_name,
        cmd, net_address, loginame, getdate() as snaptime,
        sql_handle,stmt_start,stmt_end,lastwaittype,waitresource
      into #snapshot
      from master..sysprocesses
        where uid >= 0

    select @cpu_busy = @@cpu_busy, @idle_busy = @@idle,@io_busy=@@io_busy
    waitfor delay ''00:00:01'' -- sp__perf
    select
        @cpu=   (cast(@@cpu_busy as float)- @cpu_busy)/
                ((@@idle - @idle_busy + @@cpu_busy - @cpu_busy+0.001) *1.00)
                *100.0,
        @io=    (cast(@@io_busy as float)- @io_busy)/
                1000.00 *100.0,
        @idle=  (cast(@@io_busy as float)- @io_busy)/
                ((@@idle - @idle_busy + @@io_busy - @io_busy+0.001) *1.00) *100.0

    insert into #snapshot
      select 2 as snapnum, spid, kpid, blocked,
        waittime, dbid, uid, cpu,
        physical_io, memusage, login_time, last_batch,
        ecid, status, hostname, program_name,
        cmd, net_address, loginame, getdate() as snaptime,
        sql_handle,stmt_start,stmt_end,lastwaittype,waitresource
        from master..sysprocesses
        where uid >= 0

    -- join the two snapshots, dropping ecid''s that were missing from one or the other.
    -- just do the columns that require a delta for performance.
    select
        s1.spid as s1_spid, s1.ecid as s1_ecid,
        s1.waittime as s1_waittime, s1.cpu as s1_cpu,
        s1.physical_io as s1_physical_io, s1.snaptime as s1_snaptime,
        s2.waittime as s2_waittime, s2.cpu as s2_cpu,
        s2.physical_io as s2_physical_io, s2.snaptime as s2_snaptime
      into #snapboth
      from #snapshot as s1 join #snapshot as s2
        on s1.spid = s2.spid
          and s1.ecid = s2.ecid
          and s1.snapnum = 1
          and s2.snapnum = 2

    -- get the difference between the 2 snapshots, ms per ecid
    -- this is ( / 1000 * 100 ).
    select s1_spid as spid, s1_ecid as ecid,
        sum( s2_waittime - s1_waittime ) as waitmsperecid,
        sum( s2_cpu - s1_cpu ) as cpumsperecid,
        sum( s2_physical_io - s1_physical_io ) as deltaioperecid
      into #perfdeltaperecid
      from #snapboth
      group by s1_spid, s1_ecid

    -- and sum those across per spid.
    select spid,
        sum( waitmsperecid ) as waitms,
        sum( cpumsperecid ) as cpums,
        sum( deltaioperecid ) as deltaio
      into #perfdelta
      from #perfdeltaperecid
      group by spid

    -- show the results for the cpu hogs.
    -- cpu conversion is ms to sec and then to percent / cpus.
    select top 5
        @dt as readed,
        @cpu [scpu%],@io [io%], @idle [io_idle%],
        convert( integer, pd.cpums * 0.001 * 100.0 / @icpucount ) as [pcpu%],
        pd.deltaio as [dIO], lcks.n dls,
        pd.spid, ss.kpid, convert( varchar(16), ss.loginame ) as username,
        case
            when ss.program_name like ''sqlagent - tsql jobstep (job %''
            then (
                select top 1 j.name
                from msdb..sysjobs j
                where
                    rtrim(ltrim(dbo.fn__str_between(ss.program_name,''job '','' :'',default)))
                    =
                    dbo.fn__hex(convert(varbinary,job_id))
                )
            else object_name((select top 1 objectid from ::fn_get_sql(ss.sql_handle)))
        end obj,
        substring((select top 1 text from ::fn_get_sql(ss.sql_handle)),
            coalesce(nullif(case ss.stmt_start when 0 then 0 else ss.stmt_start / 2 end, 0), 1),
            case (case ss.stmt_end when -1 then -1 else ss.stmt_end / 2 end)
                when -1
                        then datalength((select top 1 text from ::fn_get_sql(ss.sql_handle)))
                else
                        ((case ss.stmt_end when -1 then -1 else ss.stmt_end / 2 end) -
                         (case ss.stmt_start when 0 then 0 else ss.stmt_start / 2 end)
                        )
                end
        ) as [sql],
        ss.cmd,
        convert( varchar(15), ss.hostname ) as host, convert( varchar(15), ss.program_name ) as program,
        ( select name from master..sysdatabases where dbid = ss.dbid ) as db,
        ss.status, ss.login_time, ss.last_batch,
        ss.net_address,lastwaittype lwr,waitresource wr
      into #out
      from #perfdelta as pd join #snapshot as ss
          on pd.spid = ss.spid
            and ss.ecid = 0
            and ss.snapnum = 1
      left join  (
        select count(*) as n, resource_database_id dbid
        from master.sys.dm_tran_locks
        group by resource_database_id
        ) lcks on lcks.dbid=ss.dbid
      where cpums > 5
      and pd.spid!=@@spid -- and pd.spid>50 -- to exclude system spid
      order by (cpums+deltaio)/2 desc

    -- output result
    if @record=0
        begin
        select * from #out

        exec(''exec #iostats_fill'')

        -- query 5: compute the total number of reads and writes.
        select
            t2.readlatency,t2.writelatency,t2.latency,
            t2.avgbperread,t2.avgbperwrite,t2.avgbpertransfer,
            t2.db,t2.drive,t2.file_name
        from    #iostats t1
        join    #iostats t2
        on      t1.db=t2.db and t1.file_name=t2.file_name
        and     t1.capture_time < t2.capture_time
        where   t2.nwr - t1.nwr > 0
        order by latency desc

        end -- record = 0
    else
        begin
        if object_id(''tmp_stat_perf'') is null
            select identity(int,1,1) id,*
            into tmp_stat_perf
            from #out
        else
            insert tmp_stat_perf
            select * from #out
        end -- record

    break -- one day will re-implement waitfor
    end -- while

drop proc  #iostats_fill
drop table #perfdeltaperecid
drop table #perfdelta
drop table #snapboth
drop table #snapshot

/*

-- turn the raw statistics into rates
select cast(cast(@@total_read as numeric (18,2))/@secfromstart
               as numeric (18,2))  as [reads/sec]
     , cast(cast(@@total_write as numeric (18,2))/@secfromstart
               as numeric (18,2)) as [writes/sec]
     , cast(@@io_busy * cast(@@timeticks as numeric(18,2))/10000.0/@secfromstart
               as numeric (18,2)) as [percent i/o time]
*/

goto help

err_prm:    exec @ret=sp__err ''parameter gived for compatibility but not working'',@proc
            goto ret

help:
exec sp__printf ''
Scope
    get processes performance with info

Parameters
    @waitfor    (TODO) wait until this word appear in the sql code or obj name
    @opt        options
                record      store data into table tmp_stat_perf

Fields description
    scpu%:  is the whole server cpu usage
    pcpu%:  is the single process cpu usage
    dIO:    is the delta IO milliseconds
    dls:    database locks
    status: Process ID status. The possible values are:
        dormant     SQL Server is resetting the session.
        running     The session is running one or more batches.
                    When Multiple Active Result Sets (MARS) is enabled, a session can run multiple batches.
                    For more information, see Using Multiple Active Result Sets (MARS).
        background  The session is running a background task, such as deadlock detection.
        rollback    The session has a transaction rollback in process.
        pending     The session is waiting for a worker thread to become available.
        runnable    The task in the session is in the runnable queue of a scheduler while waiting to get a time quantum.
        spinloop    The task in the session is waiting for a spinlock to become free.
        suspended   The session is waiting for an event, such as I/O, to complete.
    lwr (lastwaittype):
    CXPACKET
        Often indicates nothing more than that certain queries are executing
        with parallelism; CXPACKET waits in the server are not an immediate
        sign of problems, although they may be the symptom of another problem,
        associated with one of the other high value wait types in the instance.
    SOS_SCHEDULER_YIELD
        The tasks executing in the system are yielding the scheduler, having
        exceeded their quantum, and are having to wait in the runnable queue
        for other tasks to execute. This may indicate that the server is under
        CPU pressure.
    THREADPOOL
        A task had to wait to have a worker bound to it, in order to execute.
        This could be a sign of worker thread starvation, requiring an increase
        in the number of CPUs in the server, to handle a highly concurrent
        workload, or it can be a sign of blocking, resulting in a large number
        of parallel tasks consuming the worker threads for long periods.
    LCK_*
        These wait types signify that blocking is occurring in the system and
        that sessions have had to wait to acquire a lock of a specific type,
        which was being held by another database session. This problem can be
        investigated further using, for example, the information in the
        sys.dm_db_index_operational_stats.
    PAGEIOLATCH_*, IO_COMPLETION, WRITELOG
        These waits are commonly associated with disk I/O bottlenecks, though
        the root cause of the problem may be, and commonly is, a poorly
        performing query that is consuming excessive amounts of memory in the
        server. PAGEIOLATCH_* waits are specifically associated with delays in
        being able to read or write data from the database files.
    WRITELOG
        waits are related to issues with writing to log files. These waits
        should be evaluated in conjunction with the virtual file statistics
        as well as Physical Disk performance counters, to determine if the
        problem is specific to a single database, file, or disk, or is instance
        wide.
    PAGELATCH_*
        Non-I/O waits for latches on data pages in the buffer pool. A lot of
        times PAGELATCH_* waits are associated with allocation contention issues.
        One of the best-known allocations issues associated with PAGELATCH_*
        waits occurs in tempdb when the a large number of objects are being
        created and destroyed in tempdb and the system experiences contention
        on the Shared Global Allocation Map (SGAM), Global Allocation Map (GAM),
        and Page Free Space (PFS) pages in the tempdb database.
    LATCH_*
        These waits are associated with lightweight short-term synchronization
        objects that are used to protect access to internal caches, but not the
        buffer cache. These waits can indicate a range of problems, depending on
        the latch type. Determining the specific latch class that has the most
        accumulated wait time associated with it can be found by querying the
        sys.dm_os_latch_stats DMV.
    ASYNC_NETWORK_IO
        This wait is often incorrectly attributed to a network bottleneck. In
        fact, the most common cause of this wait is a client application that
        is performing row-by-row processing of the data being streamed from SQL
        Server as a result set (client accepts one row, processes, accepts next
        row, and so on). Correcting this wait type generally requires changing
        the client-side code so that it reads the result set as fast as possible,
        and then performs processing.
''
ret:
return @ret
end -- sp__perf'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__perf:

-- ============================================================== sp__perf_daily
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__perf_daily',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091216
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__perf_daily') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__perf_daily') with nowait
        goto skip_sp__perf_daily
        end
    if @ver>091216
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__perf_daily') with nowait
        goto skip_sp__perf_daily
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__perf_daily') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__perf_daily'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__perf_daily]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091216\s.zaglio:added @@servername
    v:091018\s.zaglio:
    Statistic of SQL-Server - System parameters for dynamic collection.

    @@cpu_busy/1000 - Returns the time in seconds that the CPU has spent working since SQL Server was last started.
    @@io_busy/1000  - Returns the time in seconds that SQL Server has spent performing
                      input and output operations since it was last started.
    @@idle/1000     - Returns the time in seconds that SQL Server has been idle since last started.
    @@pack_received - Returns the number of input packets read from the network by SQL Server since last started.
    @@pack_sent     - Returns the number of output packets written to the network by SQL Server since last started.
    @@packet_errors - Returns the number of network packet errors that have occurred on
                      SQL Server connections since SQL Server was last started.
    @@connections   - Returns the number of connections, or attempted connections, since SQL Server was last started.
    @@total_read    - Returns the number of disk reads (not cache reads) by SQL Server since last started.
    @@total_write   - Returns the number of disk writes by SQL Server since last started.
    @@total_errors  - Returns the number of disk read/write errors encountered by SQL Server since last started.
    */
    /*
    Daily Version.

    Output - Table TBL_SERVERSTATISTICS. A Table include Row per run a Procedure,except Saturday.
    Sunday statistic include Saturday.
    In the table TBL_SERVERSTATISTICS_PRIOR save SQL Server statistic since last started.
    Delete history statistic rows - more than one year.
    Return Code: 1 - BAD, 0 - GOOD.

    Author  - Mushkatin Vadim,DBA,Israel.
    Created - 09/03/2003.
    -- select * from TBL_SERVERSTATISTICS
    -- select * from TBL_SERVERSTATISTICS_PRIOR
    -- sp__dir ''*tbl*''
*/
CREATE PROCEDURE [dbo].[sp__perf_daily]  ( @BIT_DELETE_RESULTS BIT = 0 )
AS

SET NOCOUNT ON

Declare @weekday         int    ,
        @count               int        ,
        @sql_started         datetime
Declare @id_prior            int        ,
        @sampletime_prior    datetime   ,
        @cpu_busy_prior      INT        ,
        @io_busy_prior       INT        ,
        @idle_prior          INT        ,
        @pack_received_prior INT        ,
        @pack_send_prior     INT        ,
        @packed_errors_prior INT        ,
        @connections_prior   INT        ,
        @total_read_prior    INT        ,
        @total_write_prior   INT        ,
        @total_errors_prior  INT

------------------------------------------
Set @sql_started   = ( select login_time  from master..sysprocesses where spid = 1 )
Set @weekday = (select DATEPART ( weekday , getdate() ))
IF  @weekday  <>  7
BEGIN
------------------------------------------
   IF OBJECT_ID(''TBL_SERVERSTATISTICS_PRIOR'') IS  NULL
      CREATE TABLE  TBL_SERVERSTATISTICS_PRIOR (
          [ID]          INT IDENTITY,
          SQL_Started   datetime,
          SAMPLETIME    datetime,
          cpu_busy      INT,
          io_busy       INT,
          idle          INT,
          pack_received INT,
          pack_send     INT,
          packed_errors INT,
          connections   INT,
          total_read    INT,
          total_write   INT,
          total_errors  INT,
          svr            sysname)

   Set @count = ( select count(*) from TBL_SERVERSTATISTICS_PRIOR )
   If @count = 0
   Begin
      TRUNCATE TABLE TBL_SERVERSTATISTICS_PRIOR
      INSERT INTO TBL_SERVERSTATISTICS_PRIOR
      select @sql_started,getdate(),@@cpu_busy/1000,@@io_busy/1000,  @@idle /1000,
             @@pack_received  ,@@pack_sent, @@packet_errors  , @@connections , @@total_read ,
             @@total_write , @@total_errors,@@servername
   End

   IF OBJECT_ID(''TBL_SERVERSTATISTICS'') IS NOT NULL
      Begin
         IF @BIT_DELETE_RESULTS = 1     --Warning !!!
            TRUNCATE TABLE TBL_SERVERSTATISTICS
   End
   ELSE
      CREATE TABLE  TBL_SERVERSTATISTICS (
          [ID]          INT IDENTITY,
          LastRunTIME   datetime,
          SAMPLETIME    datetime,
          cpu_busy      INT,
          io_busy       INT,
          idle          INT,
          pack_received INT,
          pack_send     INT,
          packed_errors INT,
          connections   INT,
          total_read    INT,
          total_write   INT,
          total_errors  INT,
          svr            sysname)

   If exists ( select sampletime  from TBL_SERVERSTATISTICS_PRIOR   where id = 1 )
      Set @sampletime_prior = (select sampletime  from TBL_SERVERSTATISTICS_PRIOR
                                                  where id = 1)
   Else  Begin
      Print ''Not found data in table TBL_SERVERSTATISTICS_PRIOR.''
      Return 1
   End

   If   @sql_started <= @sampletime_prior
   Begin
      Declare c_table_prior cursor for
     select cpu_busy, io_busy, idle, pack_received, pack_send,packed_errors,
                connections, total_read, total_write, total_errors
         FROM TBL_SERVERSTATISTICS_PRIOR
         where [id] = 1

       open c_table_prior
       fetch next from c_table_prior
          into @cpu_busy_prior,@io_busy_prior,@idle_prior,@pack_received_prior,@pack_send_prior,
               @packed_errors_prior, @connections_prior, @total_read_prior,
               @total_write_prior, @total_errors_prior

       INSERT INTO TBL_SERVERSTATISTICS
          select @sampletime_prior,getdate(),@@cpu_busy/1000 - @cpu_busy_prior,@@io_busy/1000 - @io_busy_prior,
                 @@idle/1000 - @idle_prior,@@pack_received - @pack_received_prior ,
                 @@pack_sent - @pack_send_prior , @@packet_errors - @packed_errors_prior ,
                 @@connections - @connections_prior , @@total_read - @total_read_prior,
                 @@total_write - @total_write_prior, @@total_errors - @total_errors_prior,
                 @@servername

       close c_table_prior
       deallocate c_table_prior
   END
   Else Begin
      Print ''Daily statistic Row not inserted because of REBOOT server.''
      Print ''SQL Server started - '' + cast(@sql_started as nchar(17))
   End
--since SQL Server last started
   Truncate table TBL_SERVERSTATISTICS_PRIOR
   INSERT INTO TBL_SERVERSTATISTICS_PRIOR
      select @sql_started,getdate(),@@cpu_busy/1000 ,@@io_busy/1000, @@idle/1000 ,@@pack_received ,@@pack_sent ,
             @@packet_errors ,@@connections , @@total_read,@@total_write, @@total_errors,@@servername

END  --Close First IF

--Delete history statistic rows (more than one year)
Delete
   From TBL_SERVERSTATISTICS
   Where SAMPLETIME < ( getdate() - 365 )

Return 0'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__perf_daily:

-- =============================================================== sp__perf_info
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__perf_info',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100522.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__perf_info') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__perf_info') with nowait
        goto skip_sp__perf_info
        end
    if @ver>100522.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__perf_info') with nowait
        goto skip_sp__perf_info
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__perf_info') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__perf_info'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__perf_info]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,perf
    v:100522.1000\s.zaglio: list performance counters
    t:sp__perf_info "database","trans","total",@dbg=1
*/
CREATE proc sp__perf_info
    @obj    sysname = null,
    @cnt    sysname = null,
    @inst   sysname = null,
    @dbg int=0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    @sql nvarchar(4000),
    @objects nvarchar(4000),
    @counters nvarchar(4000),
    @instances nvarchar(4000),
    @tbl sysname
-- =========================================================== initialization ==

select @objects=''select distinct object_name as objects from %tbl% order by 1''
select @counters=''select distinct counter_name as counters from %tbl% order by 1''
select @instances=''select distinct instance_name as instances from %tbl% order by 1''
select
    @obj=isnull(@obj,''''),
    @cnt=isnull(@cnt,''''),
    @inst=isnull(@inst,'''')

select @sql=''
select * from %tbl%
where 1=1
and object_name like ''''%%obj%%''''
and counter_name like ''''%%cnt%%''''
and instance_name like ''''%%inst%%''''
''

if dbo.fn__isMSSQL2K()=1 select @tbl=''sysperfinfo''
else select @tbl=''sys.dm_os_performance_counters''

exec sp__str_replace @objects out,''%tbl%'',@tbl
exec sp__str_replace @counters out,''%tbl%'',@tbl
exec sp__str_replace @instances out,''%tbl%'',@tbl
exec sp__str_replace @sql out,''%tbl%|%obj%|%cnt%|%inst%'',@tbl,@obj,@cnt,@inst

-- ======================================================== second params chk ==

if @obj='''' and @cnt='''' and @inst='''' goto help

-- ===================================================================== body ==
if @dbg=1 exec sp__printsql @sql
exec(@sql)

goto ret

-- =================================================================== errors ==

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    list performance counters

Parameters
    @obj,

Examples
    -- show total transactions
    sp__perf_info "database","trans","total"

-- Counter: Recommended value
SQLServer:Memory Manager\Memory Grants Pending:0
SQServer:Buffer Manager\Buffer Hit cache Ratio:>95%
SQLServer:Buffer Manager\Page life Expectancy:>300
SQLServer:Buffer Manager\Free Pages:>300
SQLServer:Buffer Manager\Free list Stalls/Sec:0
Memory Grants Pending represents the total number of processes waiting for a workspace memory grant, a non-zero value indicates a memory problem
Buffer Hit Cache Ratio represets the percentage of time SQL was able to get the page from buffer pool instead of having to do a hard read from disk
Page Life Expectancy shows how long a data page stays in the buffer cache. 300 seconds is the industry-accepted threshold for this counter. Anything less than a 300-second average over an extended period of time tells you that the data pages are being flushed from memory too frequently and SQL has to do hard reads from disk fullfill requests, hard reads require CPU/lock resources etc. if there is a sudden drop in Page life expectancy then we need to check Checkpoints Pages/sec counter When a checkpoint occurs in the system, the dirty data pages in the buffer cache are flushed to disk, causing the Page life Expectancy value to drop.
Free Pages represent the total number of pages on all free lists, this number in general should be above 300 the hight the better, a lower number would be ok if Free list stalls/sec is a zero value.
Free list Stalls/Sec represents Number of requests per second that had to wait for a free page.

if you have defined that your server is under memory pressure the second thing to do to check the components that consume the memory.

-- list of objects, counters, instances --
''
exec sp__select_astext @objects,@header=1
exec sp__printf ''''
exec sp__select_astext @counters,@header=1
exec sp__printf ''''
exec sp__select_astext @instances,@header=1

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__perf_info'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__perf_info:

-- ============================================================ sp__perf_monitor
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__perf_monitor',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140109
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__perf_monitor') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__perf_monitor') with nowait
        goto skip_sp__perf_monitor
        end
    if @ver>140109
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__perf_monitor') with nowait
        goto skip_sp__perf_monitor
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__perf_monitor') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__perf_monitor'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__perf_monitor]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:140109\s.zaglio:problem of cpu_ticks_in_ms in os 64bit
    r:110922\s.zaglio:sp__perf_monitor
*/
CREATE proc sp__perf_monitor
    @n tinyint = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @opt is null goto help

-- ============================================================== declaration ==
-- declare -- insert before here --  @end_declare bit
declare
    @ts_now bigint,
    @from datetime,@to datetime,@sql int,@idle int,@other int

-- =========================================================== initialization ==
-- select -- insert before here --  @end_declare=1
-- ======================================================== second params chk ==
-- ===================================================================== body ==

/*
- HIGH CPU *******
      -- Isolate top waits for server instance
      WITH Waits AS
      (
        SELECT
            wait_type,
            wait_time_ms / 1000. AS wait_time_s,
            100. * wait_time_ms / SUM(wait_time_ms) OVER() AS pct,
            ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS rn
        FROM sys.dm_os_wait_stats
        WHERE wait_type NOT LIKE ''%SLEEP%''
      )
      SELECT
        W1.wait_type,
        CAST(W1.wait_time_s AS DECIMAL(12, 2)) AS wait_time_s,
        CAST(W1.pct AS DECIMAL(12, 2)) AS pct,
        CAST(SUM(W2.pct) AS DECIMAL(12, 2)) AS running_pct
      FROM Waits AS W1
      INNER JOIN Waits AS W2
      ON W2.rn <= W1.rn
      GROUP BY W1.rn, W1.wait_type, W1.wait_time_s, W1.pct
      HAVING SUM(W2.pct) - W1.pct < 90 -- percentage threshold
      ORDER BY W1.rn;
*/
/*
create table #waits (type varchar(128), req bigint, waittime bigint, signal bigint)
truncate table #waits
insert into #waits exec(''dbcc sqlperf(waitstats)'')
    --insert into _LakeSide_DbTools_WaitsLogger_WaitsLog (DT,CPU,Locks,Reads,Writes,Network,PhReads,PhWrites,LgReads)
select
    getdate() AS DT,
    CAST(@@CPU_BUSY * CAST(@@TIMETICKS AS FLOAT) / 1000 AS BIGINT) as CPU,  -- in milliseconds
    sum(convert(bigint, case when type like ''LCK%''
      then waittime else 0 end)) as Locks,
    sum(convert(bigint, case when type like ''LATCH%''  or type like ''PAGELATCH%'' or type like ''PAGEIOLATCH%''
      then waittime else 0 end)) as Reads,
    sum(convert(bigint, case when type like ''%IO_COMPLETION%'' or type=''WRITELOG''
      then waittime else 0 end)) as Writes,
    sum(convert(bigint, case when type in (''NETWORKIO'',''OLEDB'')
      then waittime else 0 end)) as Network,
    @@TOTAL_READ AS PhReads, @@TOTAL_WRITE AS PhWrites,
        ISNULL((select cntr_value from master.dbo.sysperfinfo where counter_name=''Page lookups/sec''), 0) AS LgReads
-- select *
from #waits
truncate table #waits
insert into #waits exec(''dbcc sqlperf(waitstats)'')
    --insert into _LakeSide_DbTools_WaitsLogger_WaitsLog (DT,CPU,Locks,Reads,Writes,Network,PhReads,PhWrites,LgReads)
select
    getdate() AS DT,
    CAST(@@CPU_BUSY * CAST(@@TIMETICKS AS FLOAT) / 1000 AS BIGINT) as CPU,  -- in milliseconds
    sum(convert(bigint, case when type like ''LCK%''
      then waittime else 0 end)) as Locks,
    sum(convert(bigint, case when type like ''LATCH%''  or type like ''PAGELATCH%'' or type like ''PAGEIOLATCH%''
      then waittime else 0 end)) as Reads,
    sum(convert(bigint, case when type like ''%IO_COMPLETION%'' or type=''WRITELOG''
      then waittime else 0 end)) as Writes,
    sum(convert(bigint, case when type in (''NETWORKIO'',''OLEDB'')
      then waittime else 0 end)) as Network,
    @@TOTAL_READ AS PhReads, @@TOTAL_WRITE AS PhWrites,
        ISNULL((select cntr_value from master.dbo.sysperfinfo where counter_name=''Page lookups/sec''), 0) AS LgReads
-- select *
from #waits
*/

-- collect processord usage of last 4 hours
-- declare @ts_now bigint
exec sp_executesql N''
    select @ts_now = cpu_ticks / convert(float, cpu_ticks_in_ms) from sys.dm_os_sys_info
    '',N''@ts_now bigint out'',@ts_now=@ts_now out

select
    record_id,
    dateadd(ms, -1 * ( @ts_now - [timestamp] ), getdate()) as eventtime,
    sqlprocessutilization,
    systemidle,
    100 - systemidle - sqlprocessutilization as otherprocessutilization
into #usage
from (
    select -- !!!! values of record.value are case sensitive
        record.value(''(./Record/@id)[1]'', ''int'') AS record_id,
        record.value(''(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]'',''int'') AS systemidle,
        record.value(''(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]'', ''int'') AS sqlprocessutilization,
        timestamp
    from (
        select
            timestamp,
            convert(xml, record) as record
        from sys.dm_os_ring_buffers
        where ring_buffer_type = N''ring_buffer_scheduler_monitor''
        and record like ''%%'') as x
    ) as y
order by record_id desc

select
    @from=min(eventtime),
    @to=max(eventtime),
    @sql=avg(sqlprocessutilization),
    @idle=avg(systemidle),
    @other=avg(otherprocessutilization)
from #usage

if @sql is null goto err_ver

exec sp__printf ''
Avg of last 4 hours of cpu usage:
    from:%s     to:%s
    mssql : %d
    idle  : %d
    other : %d'',
    @from,@to,@sql,@idle,@other

select @n=30
while (@n>0)
    begin
    exec sp__printf ''Waiting for MSSQL exceed 80% of CPU usage''
    select @sql=0
    while (@sql<81)
        select top 1 @sql=sqlprocessutilization
        from (
            select -- !!!! values of record.value are case sensitive
                record.value(''(./Record/@id)[1]'', ''int'') AS record_id,
                record.value(''(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]'', ''int'') AS sqlprocessutilization
            from (
                select
                    timestamp,
                    convert(xml, record) as record
                from sys.dm_os_ring_buffers
                where ring_buffer_type = N''ring_buffer_scheduler_monitor''
                and record like ''%%'') as x
            ) as y
        order by record_id desc

    -- SELECT *  FROM sys.dm_os_threads  WHERE started_by_sqlservr = 0;


    -- show thread usage
    select p.spid,t.kernel_time,t.usermode_time,getdate() d,sql_handle
    into #a
    from sys.dm_os_threads t
    join master..sysprocesses p
    on p.kpid=os_thread_id
    where p.spid>50

    waitfor delay ''00:00:001''

    select p.spid,t.kernel_time,t.usermode_time,getdate() d,sql_handle
    into #b
    from sys.dm_os_threads t
    join master..sysprocesses p
    on p.kpid=os_thread_id
    where p.spid>50

    select
        getdate() dt,
        a.spid,
        b.kernel_time-a.kernel_time+b.usermode_time-a.usermode_time times,
        datediff(ms,a.d,b.d) ms,
        db_name(t.dbid) db,
        object_name(t.objectid) /*,t.dbid)*/ obj,
        t.text
    into #c
    from #a a
    join #b b on a.spid=b.spid and a.sql_handle=b.sql_handle
    cross apply sys.dm_exec_sql_text (a.sql_handle) t
    where
        b.kernel_time-a.kernel_time+b.usermode_time-a.usermode_time>1000

    if @@rowcount>0
        begin
        select * from #c order by times desc
        select @n=@n-1
        end

    drop table #a drop table #b drop table #c

    -- see also sys.dm_os_workers

    end -- infinite loop

goto ret

-- =================================================================== errors ==
err_ver:
    exec @ret=sp__err ''this version of MSSQL do not support completelly '',@proc
    goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    [write here a short desc]

Parameters
    [param]     [desc]

Examples
    [exam ple]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__perf_monitor'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__perf_monitor:

-- =============================================================== sp__perf_shot
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__perf_shot',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130228
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__perf_shot') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__perf_shot') with nowait
        goto skip_sp__perf_shot
        end
    if @ver>130228
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__perf_shot') with nowait
        goto skip_sp__perf_shot
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__perf_shot') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__perf_shot'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__perf_shot]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:performance, snapshot,
    r:130228\s.zaglio: take a snapshot of system for performance evaluation
    t:sp__perf_shot run
*/
CREATE proc sp__perf_shot
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
        |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if  @run=0 goto help


-- =============================================================== #tbls init ==

-- ===================================================================== body ==

-- show if phisical disk is involved in I/O bottleneck(?)
-- heavy i/o
select
    db_name(t1.database_id) db,
    file_name(t1.file_id) fname,
    t1.io_stall,
    t2.io_pending_ms_ticks,
    object_name(sql.objectid) obj,
    case when sql_handle is null then '' ''
    else ( substring(sql.text,(er.statement_start_offset+2)/2,(case when er.statement_end_offset = -1 then
    len(convert(nvarchar(max),sql.text))*2 else er.statement_end_offset end - er.statement_start_offset) /2 ) )
    end as stm,
    text as sql
from sys.dm_io_virtual_file_stats(NULL, NULL)t1
join sys.dm_io_pending_io_requests as t2  on t1.file_handle = t2.io_handle
left join sys.dm_os_schedulers os on t2.scheduler_address=os.scheduler_address
left join sys.dm_os_workers ow on os.active_worker_address=ow.worker_address
left join sys.dm_exec_requests er on er.task_address=ow.task_address
cross apply sys.dm_exec_sql_text(er.sql_handle) as sql
where io_pending_ms_ticks>20

-- batch that generate most i/o in general
select top 10
    (total_logical_reads/execution_count) as avg_logical_reads,
    (total_logical_writes/execution_count) as avg_logical_writes,
    (total_physical_reads/execution_count) as avg_phys_reads,
     Execution_count,
    statement_start_offset as stmt_start_offset,
    --sql_handle,
    --plan_handle,
    st.text,
    object_name(st.objectid) obj,
    case when sql_handle is null then '' ''
    else ( substring(st.text,(qs.statement_start_offset+2)/2,(case when qs.statement_end_offset = -1 then
    len(convert(nvarchar(max),st.text))*2 else qs.statement_end_offset end - qs.statement_start_offset) /2 ) )
    end as stm
from sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text( sql_handle) st
order by
 (total_logical_reads + total_logical_writes) Desc -- in general

-- batch that generate most i/o in single execution
select top 10
    (total_logical_reads/execution_count) as avg_logical_reads,
    (total_logical_writes/execution_count) as avg_logical_writes,
    (total_physical_reads/execution_count) as avg_phys_reads,
     Execution_count,
    -- statement_start_offset as stmt_start_offset,
    --sql_handle,
    --plan_handle,
    object_name(st.objectid) obj,
    case when sql_handle is null then '' ''
    else ( substring(st.text,(qs.statement_start_offset+2)/2,(case when qs.statement_end_offset = -1 then
    len(convert(nvarchar(max),st.text))*2 else qs.statement_end_offset end - qs.statement_start_offset) /2 ) )
    end as stm,
    st.text
from sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text( sql_handle) st
order by
 (total_logical_reads + total_logical_writes)/execution_count

-- the following query shows the top 50 sql statements with high average cpu consumption.
select top 10
total_worker_time/execution_count as [avg cpu time],
object_name(objectid) obj,
substring(text,statement_start_offset/2,(case when statement_end_offset = -1 then len(convert(nvarchar(max), text)) * 2 else statement_end_offset end -statement_start_offset)/2) as sql
from sys.dm_exec_query_stats
cross apply sys.dm_exec_sql_text(sql_handle)
order by [avg cpu time] desc

-- How to determine whether any active requests are running in parallel for a given session
Select
    object_name(sql.objectid) obj,
    substring(text,statement_start_offset/2,(case when statement_end_offset = -1 then len(convert(nvarchar(max), text)) * 2 else statement_end_offset end -statement_start_offset)/2) as sql,
    r.session_id, r.request_id, max(isnull(exec_context_id, 0)) as number_of_workers,
    r.sql_handle,r.statement_start_offset,r.statement_end_offset, r.plan_handle
from sys.dm_exec_requests r
join sys.dm_os_tasks t on r.session_id = t.session_id
join sys.dm_exec_sessions s on r.session_id = s.session_id
cross apply sys.dm_exec_sql_text(sql_handle) as sql
where s.is_user_process = 0x1
group by r.session_id, r.request_id, r.sql_handle, r.plan_handle,
r.statement_start_offset, r.statement_end_offset,sql.objectid,sql.text
having max(isnull(exec_context_id, 0)) > 0

-- 6. How to find the total number of worker threads in the runnable state (Waiting for CPU)?
select count(*) as workers_waiting_for_cpu, s.scheduler_id
from sys.dm_os_workers as o inner join sys.dm_os_schedulers as s
on o.scheduler_address = s.scheduler_address and s.scheduler_id < 255
where o.state = ''runnable'' group by s.scheduler_id

-- 7. query gives you the top 25 stored procedures that have been recompiled. The plan_generation_num indicates the number of times the query has recompiled.
select top 25
      db_name(dbid) db,
      object_name(sql_text.objectid) obj,
      sql_text.text,
      -- sql_handle,
      sum(plan_generation_num) as plan_generation_num,
      sum(execution_count) execution_count
from sys.dm_exec_query_stats a
cross apply sys.dm_exec_sql_text(sql_handle) as sql_text
where plan_generation_num > 1
group by dbid,sql_text.objectid,sql_text.text
order by plan_generation_num desc

-- 8. How to find the all sessions on SQL Server 2005?
select s.session_id , s.login_time , s.host_name, s.program_name, s.cpu_time / 1000.0 as cpu_time, s.memory_usage*8 as memory_usage
, s.login_name, s.nt_domain, s.nt_user_name, c.connect_time, c.num_reads, c.num_writes, c.client_net_address
, c.client_tcp_port, c.session_id
, case when r.sql_handle is not null then (select top 1 SUBSTRING(t2.text, (r.statement_start_offset + 2) / 2, ( (case when
r.statement_end_offset = -1 then ((len(convert(nvarchar(MAX),t2.text))) * 2) else r.statement_end_offset end) - r.statement_start_offset) /
2) from sys.dm_exec_sql_text(r.sql_handle) t2 )
else ''''
end as sql_statement
from sys.dm_exec_sessions s
left outer join sys.dm_exec_connections c on ( s.session_id = c.session_id )
left outer join sys.dm_exec_requests r on ( r.session_id = c.session_id and r.connection_id = c.connection_id )
where s.is_user_process = 1

-- How to find the TEMP DB space availability?
select sum (user_object_reserved_page_count)*8 as user_objects_kb,
sum (internal_object_reserved_page_count)*8 as internal_objects_kb,
sum (version_store_reserved_page_count)*8 as version_store_kb,
sum (unallocated_extent_page_count)*8 as freespace_kb
-- select *
from sys.dm_db_file_space_usage
where database_id = 2

-- 12. How to list the Queries by total IO?
select top 10 rank() over (order by total_logical_reads+total_logical_writes desc,sql_handle,statement_start_offset ) as row_no
, creation_time, last_execution_time, (total_worker_time+0.0)/1000 as total_worker_time
, (total_worker_time+0.0)/(execution_count*1000) as [AvgCPUTime]
, total_logical_reads as [LogicalReads], total_logical_writes as [LogicalWrites]
, execution_count, total_logical_reads+total_logical_writes as [AggIO]
, (total_logical_reads+total_logical_writes)/(execution_count+0.0) as [AvgIO]
, case when sql_handle IS NULL then '' ''
else ( substring(st.text,(qs.statement_start_offset+2)/2,(case when qs.statement_end_offset = -1 then
len(convert(nvarchar(MAX),st.text))*2 else qs.statement_end_offset end - qs.statement_start_offset) /2 ) )
end as query_text
, db_name(st.dbid) as database_name
, st.objectid as object_id
, object_name(st.objectid) obj
from sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text(sql_handle) st
where total_logical_reads+total_logical_writes > 0
order by [AggIO] desc

-- 11. How to list the queries by Total CPU Time?
select rank() over(order by total_worker_time desc,sql_handle,statement_start_offset) as row_no
, creation_time, last_execution_time, (total_worker_time+0.0)/1000 as total_worker_time
, (total_worker_time+0.0)/(execution_count*1000) as [AvgCPUTime]
, total_logical_reads as [LogicalReads], total_logical_writes as [logicalWrites]
, execution_count, total_logical_reads+total_logical_writes as [AggIO]
, (total_logical_reads+total_logical_writes)/(execution_count + 0.0) as [AvgIO]
, case when sql_handle IS NULL
then '' ''
else ( substring(st.text,(qs.statement_start_offset+2)/2,(case when qs.statement_end_offset = -1 then
len(convert(nvarchar(MAX),st.text))*2 else qs.statement_end_offset end - qs.statement_start_offset) /2 ) )
end as query_text
, db_name(st.dbid) as database_name
, st.objectid as object_id
, object_name(st.objectid) obj
from sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text(sql_handle) st
where total_worker_time > 0
order by total_worker_time desc

-- show wait types
select top 10 *
from sys.dm_os_wait_stats
--where wait_type not in (''CLR_SEMAPHORE'',''LAZYWRITER_SLEEP'',''RESOURCE_QUEUE'',''SLEEP_TASK'',''SLEEP_SYSTEMTASK'',''WAITFOR'')
order by wait_time_ms desc

begin try
declare @tab_tran_locks as table(
l_resource_type nvarchar(60) collate database_default
, l_resource_subtype nvarchar(60) collate database_default
, l_resource_associated_entity_id bigint
, l_blocking_request_spid int, l_blocked_request_spid int, l_blocking_request_mode nvarchar(60) collate database_default
, l_blocked_request_mode nvarchar(60) collate database_default, l_blocking_tran_id bigint, l_blocked_tran_id bigint
);
declare @tab_blocked_tran as table (
tran_id bigint
, no_blocked bigint
);
declare @temp_tab table(
blocking_status int, no_blocked int, l_resource_type nvarchar(60) collate database_default
, l_resource_subtype nvarchar(60) collate database_default , l_resource_associated_entity_id bigint
, l_blocking_request_spid int, l_blocked_request_spid int, l_blocking_request_mode nvarchar(60) collate database_default
, l_blocked_request_mode nvarchar(60) collate database_default , l_blocking_tran_id int, l_blocked_tran_id int
, local1 int, local2 int, b_tran_id bigint, w_tran_id bigint, b_name nvarchar(128) collate database_default
, w_name nvarchar(128) collate database_default , b_tran_begin_time datetime, w_tran_begin_time datetime
, b_state nvarchar(60) collate database_default , w_state nvarchar(60) collate database_default
, b_trans_type nvarchar(60) collate database_default , w_trans_type nvarchar(60) collate database_default
, b_text nvarchar(max) collate database_default , w_text nvarchar(max) collate database_default
, db_span_count1 int, db_span_count2 int
);

insert into @tab_tran_locks
select a.resource_type, a.resource_subtype, a.resource_associated_entity_id, a.request_session_id as blocking
, b.request_session_id as blocked, a.request_mode, b.request_mode , a.request_owner_id
, b.request_owner_id
from sys.dm_tran_locks a join sys.dm_tran_locks b on (a.resource_type = b.resource_type and a.resource_subtype = b.resource_subtype and
a.resource_associated_entity_id = b.resource_associated_entity_id and a.resource_description = b.resource_description)
where a.request_status = ''GRANT'' and (b.request_status = ''WAIT'' or b.request_status = ''CONVERT'') and a.request_owner_type = ''TRANSACTION''
and b.request_owner_type = ''TRANSACTION''

insert into @tab_blocked_tran
select ttl.l_blocking_tran_id, count(distinct ttl.l_blocked_tran_id)
from @tab_tran_locks ttl
group by ttl.l_blocking_tran_id
order by count( distinct ttl.l_blocked_tran_id) desc

insert into @temp_tab
select 0 as blocking_status, tbt.no_blocked, ttl.*, st1.is_local as local1, st2.is_local as local2
, st1.transaction_id as b_tran_id, st2.transaction_id as w_tran_id, at1.name as b_name,at2.name as w_name
, at1.transaction_begin_time as b_tran_begin_time, at2.transaction_begin_time as w_tran_begin_time
, case when at1.transaction_type <> 4 then case at1.transaction_state
when 0 then ''Invalid'' when 1 then ''Initialized''
when 2 then ''Active'' when 3 then ''Ended''
when 4 then ''Commit Started'' when 5 then ''Prepared''
when 6 then ''Committed'' when 7 then ''Rolling Back''
when 8 then ''Rolled Back''
end
else case at1.dtc_state
when 1 then ''Active'' when 2 then ''Prepared''
when 3 then ''Committed'' when 4 then ''Aborted''
when 5 then ''Recovered''
end end b_state
, case when at2.transaction_type <> 4 then case at2.transaction_state
when 0 then ''Invalid'' when 1 then ''Initialized''
when 2 then ''Active'' when 3 then ''Ended''
when 4 then ''Commit Started'' when 5 then ''Prepared''
when 6 then ''Committed'' when 7 then ''Rolling Back''
when 8 then ''Rolled Back'' end
else case at2.dtc_state
when 1 then ''Active'' when 2 then ''Prepared''
when 3 then ''Committed'' when 4 then ''Aborted''
when 5 then ''Recovered'' end
end w_state, at1.transaction_type as b_trans_type
, at2.transaction_type as w_trans_type, case when r1.sql_handle IS NULL then ''--'' else ( select top 1 substring(text,(r1.statement_start_offset+2)/2, (case when r1.statement_end_offset = -1 then len(convert(nvarchar(MAX),text))*2 else r1.statement_end_offset end - r1.statement_start_offset) /2 )
from sys.dm_exec_sql_text(r1.sql_handle)) end as b_text, case when r2.sql_handle IS NULL then ''--'' else ( select top 1 substring(text,(r2.statement_start_offset+2)/2, (case when r2.statement_end_offset = -1 then len(convert(nvarchar(MAX),text))*2 else r2.statement_end_offset end - r2.statement_start_offset) /2 ) from sys.dm_exec_sql_text(r2.sql_handle)) end as w_text
, ( Select count(distinct database_id) from sys.dm_tran_database_transactions where transaction_id = st1.transaction_id ) as
db_span_count1, ( Select count(distinct database_id) from sys.dm_tran_database_transactions where transaction_id = st2.transaction_id ) as
db_span_count2
from @tab_tran_locks ttl
inner join sys.dm_tran_active_transactions at1 on(at1.transaction_id = ttl.l_blocking_tran_id)
inner join @tab_blocked_tran tbt on(tbt.tran_id = at1.transaction_id)
inner join sys.dm_tran_session_transactions st1 on(at1.transaction_id = st1.transaction_id)
left outer join sys.dm_exec_requests r1 on(at1.transaction_id = r1.transaction_id )
inner join sys.dm_tran_active_transactions at2 on(at2.transaction_id = ttl.l_blocked_tran_id)
inner join sys.dm_tran_session_transactions st2 on(at2.transaction_id = st2.transaction_id)
left outer join sys.dm_exec_requests r2 on(at2.transaction_id = r2.transaction_id )
where st1.is_user_transaction = 1 and st2.is_user_transaction = 1 order by tbt.no_blocked desc;

with Blocking( blocking_status, no_blocked, total_blocked, l_resource_type, l_resource_subtype
, l_resource_associated_entity_id, l_blocking_request_spid, l_blocked_request_spid, l_blocking_request_mode
, l_blocked_request_mode, local1, local2, b_tran_id, w_tran_id, b_name, w_name, b_tran_begin_time
, w_tran_begin_time, b_state, w_state, b_trans_type, w_trans_type, b_text, w_text, db_span_count1
, db_span_count2, lvl)
as( select blocking_status
, no_blocked , no_blocked , l_resource_type , l_resource_subtype , l_resource_associated_entity_id
, l_blocking_request_spid , l_blocked_request_spid , l_blocking_request_mode , l_blocked_request_mode
, local1 , local2 , b_tran_id , w_tran_id , b_name , w_name , b_tran_begin_time
, w_tran_begin_time , b_state , w_state , b_trans_type , w_trans_type , b_text
, w_text , db_span_count1 , db_span_count2 , 0 from @temp_tab
union all select E.blocking_status
, M.no_blocked , convert(int,E.no_blocked + total_blocked) , E.l_resource_type
, E.l_resource_subtype , E.l_resource_associated_entity_id , M.l_blocking_request_spid
, E.l_blocked_request_spid , M.l_blocking_request_mode , E.l_blocked_request_mode
, M.local1 , E.local2 , M.b_tran_id , E.w_tran_id , M.b_name , E.w_name
, M.b_tran_begin_time , E.w_tran_begin_time , M.b_state , E.w_state , M.b_trans_type
, E.w_trans_type , M.b_text , E.w_text , M.db_span_count1 , E.db_span_count2
, M.lvl+1
from @temp_tab as E
join Blocking as M on E.b_tran_id = M.w_tran_id
)

select *
from Blocking
order by no_blocked desc,b_tran_id,w_tran_id ;
end try
begin catch
select
ERROR_SEVERITY() as blocking_status
, ERROR_STATE() as no_blocked
, ERROR_MESSAGE() as total_blocked
, 1 as l_resource_type,1 as l_resource_subtype,1 as l_resource_associated_entity_id,1 as l_blocking_request_spid,1 as
l_blocked_request_spid,1 as l_blocking_request_mode,1 as l_blocked_request_mode,1 as local1,1 as local2,1 as b_tran_id,1 as w_tran_id,1 as
b_name,1 as w_name,1 as b_tran_begin_time,1 as w_tran_begin_time,1 as b_state,1 as w_state,1 as b_trans_type,1 as w_trans_type,1 as b_text,1
as w_text,1 as db_span_count1,1 as db_span_count2,1 as lvl
end catch

goto dispose

-- shows some operators that may be CPU intensive, such as ‘%Hash Match%’, ‘%Sort%’ to look for suspects.
select object_name(objectid) obj,*
from
      sys.dm_exec_cached_plans
      cross apply sys.dm_exec_query_plan(plan_handle)
where
      cast(query_plan as nvarchar(max)) like ''%Sort%''
      or cast(query_plan as nvarchar(max)) like ''%Hash Match%''


-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    take a snapshot of system to do some evaluation about performance

Parameters
    @opt    options
            run     execute

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__perf_shot'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__perf_shot:

-- ============================================================= sp__perf_worsts
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__perf_worsts',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120609
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__perf_worsts') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__perf_worsts') with nowait
        goto skip_sp__perf_worsts
        end
    if @ver>120609
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__perf_worsts') with nowait
        goto skip_sp__perf_worsts
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__perf_worsts') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__perf_worsts'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__perf_worsts]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,perf
    v:120609\s.zaglio: refine
    r:120113\s.zaglio: show worst queryes
    c:
        orinally from
        http://www.databasejournal.com/features/mssql/article.php/3802936/
               Finding-the-Worst-Performing-T-SQL-Statements-on-an-Instance.htm
        written by: gregory a. larsen
        copyright © 2008 gregory a. larsen.  all rights reserved.
*/
CREATE proc sp__perf_worsts
    @opt sysname = null,
    @dbg int     = null
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @opt=''||'' goto help

-- ============================================================== declaration ==
declare
    @dbname varchar(128),@count int,@orderby varchar(4)

-- =========================================================== initialization ==
select
    @dbname = ''<not supplied>'',
    @count = 999999999,
    @orderby = ''aio''
-- ======================================================== second params chk ==
-- ===================================================================== body ==

exec sp__printf ''-- collecting info...''
/* check for valid @orderby parameter
if ((select case when
          @orderby in (''acpu'',''tcpu'',''ae'',''te'',''ec'',''aio'',''tio'',''alr'',''tlr'',''alw'',''tlw'',''apr'',''tpr'')
             then 1 else 0 end) = 0)
begin
   -- abort if invalid @orderby parameter entered
   raiserror(''@orderby parameter not apcu, tcpu, ae, te, ec, aio, tio, alr, tlr, alw, tlw, apr or tpr'',11,1)
   return
 end
 */
select top (@count)
     coalesce(db_name(st.dbid),
              db_name(cast(pa.value as int))+''*'',
             ''resource'') as [database name]
     -- find the offset of the actual statement being executed
     ,substring(text,
               case when statement_start_offset = 0
                      or statement_start_offset is null
                       then 1
                       else statement_start_offset/2 + 1 end,
               case when statement_end_offset = 0
                      or statement_end_offset = -1
                      or statement_end_offset is null
                       then len(text)
                       else statement_end_offset/2 end -
                 case when statement_start_offset = 0
                        or statement_start_offset is null
                         then 1
                         else statement_start_offset/2  end + 1
              )  as [statement]
     ,object_schema_name(st.objectid,dbid) [schema name]
     ,object_name(st.objectid,dbid) [object name]
     ,objtype [cached plan objtype]
     ,execution_count [execution count]
     ,(total_logical_reads + total_logical_writes + total_physical_reads )/execution_count [average ios]
     ,total_logical_reads + total_logical_writes + total_physical_reads [total ios]
     ,total_logical_reads/execution_count [avg logical reads]
     ,total_logical_reads [total logical reads]
     ,total_logical_writes/execution_count [avg logical writes]
     ,total_logical_writes [total logical writes]
     ,total_physical_reads/execution_count [avg physical reads]
     ,total_physical_reads [total physical reads]
     ,total_worker_time / execution_count [avg cpu]
     ,total_worker_time [total cpu]
     ,total_elapsed_time / execution_count [avg elapsed time]
     ,total_elapsed_time  [total elasped time]
     ,last_execution_time [last execution time]
into #perf_worsts
from sys.dm_exec_query_stats qs
join sys.dm_exec_cached_plans cp on qs.plan_handle = cp.plan_handle
cross apply sys.dm_exec_sql_text(qs.plan_handle) st
outer apply sys.dm_exec_plan_attributes(qs.plan_handle) pa
where attribute = ''dbid'' and
 case when @dbname = ''<not supplied>'' then ''<not supplied>''
                           else coalesce(db_name(st.dbid),
                                      db_name(cast(pa.value as int)) + ''*'',
                                      ''resource'') end
                                in (rtrim(@dbname),rtrim(@dbname) + ''*'')

select *
from #perf_worsts
order by
case
    when @orderby = ''acpu'' then [total cpu]  / [execution count]
    when @orderby = ''tcpu''  then [total cpu]
    when @orderby = ''ae''   then [total elasped time] / [execution count]
    when @orderby = ''te''   then [total elasped time]
    when @orderby = ''ec''   then [execution count]
    when @orderby = ''aio''  then ([total logical reads] + [total logical writes] + [total physical reads]) / [execution count]
    when @orderby = ''tio''  then [total logical reads] + [total logical writes] + [total physical reads]
    when @orderby = ''alr''  then [total logical reads]  / [execution count]
    when @orderby = ''tlr''  then [total logical reads]
    when @orderby = ''alw''  then [total logical writes] / [execution count]
    when @orderby = ''tlw''  then [total logical writes]
    when @orderby = ''apr''  then [total physical reads] / [execution count]
    when @orderby = ''tpr''  then [total physical reads]
end desc

goto ret
-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    this stored procedure displays the top worst performing queries based on cpu, execution count,
    i/o and elapsed_time as identified using dmv information.  this can be display the worst
    performing queries from an instance, or database perspective.   the number of records shown,
    the database, and the sort order are identified by passing pararmeters.

Parameters
    @opt    options
            run     run collection of performance info
    @dbg    not used

Notes
    "acpu" represents average cpu usage
    "tcpu" represents total cpu usage
    "ae"   represents average elapsed time
    "te"   represents total elapsed time
    "ec"   represents execution count
    "aio"  represents average ios
    "tio"  represents total ios
    "alr"  represents average logical reads
    "tlr"  represents total logical reads
    "alw"  represents average logical writes
    "tlw"  represents total logical writes
    "apr"  represents average physical reads
    "tpr"  represents total physical read

Examples
    exec sp__perf_worsts @code=''''sp__perf_worsts''''
''
/*
   top 6 statements in the adventureworks database base on average cpu usage:
      exec usp_worst_tsql @dbname=''adventureworks'',@count=6,@orderby=''acpu'';

   top 100 statements order by average io
      exec usp_worst_tsql @count=100,@orderby=''alr'';

   show top all statements by average io
      exec usp_worst_tsql;
*/
select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- sp__perf_worsts'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__perf_worsts:

-- ==================================================================== sp__pkey
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__pkey',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100523
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__pkey') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__pkey') with nowait
        goto skip_sp__pkey
        end
    if @ver>100523
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__pkey') with nowait
        goto skip_sp__pkey
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__pkey') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__pkey'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__pkey]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100523\s.zaglio: a bug near counts and added excludes
    v:100321\s.zaglio: return pkey''s fields or finds possibles
    t:sp__pkey ''MATMAS_MARAM'',@dbg=1
    t:
        sp__pkey ''MATMAS04_MARA_MTXH_MTXL''
        select * from MATMAS04_MARA_MTXH_MTXL
        sios_select ''*'',''MTXH''
*/
CREATE proc sp__pkey
    @tbl sysname=null,
    @pk nvarchar(512)=null,
    @excludes sysname=null,
    @dbg bit=0
as
begin
declare @proc sysname,@ret int
select @proc=''sp__pkey'',@ret=0
if @tbl is null goto help

declare
    @flds nvarchar(4000),@i int,@n int,@nr bigint,@r bigint,
    @sql nvarchar(4000),@fld sysname,@pks int

select @flds=dbo.fn__flds_of(@tbl,'','',@excludes)
if @dbg=1 exec sp__printf ''%s'',@flds
select @i=1,@n=dbo.fn__str_count(@flds,'','')

exec sp__count @tbl,@nr out
select @pks=0

while (@i<=@n)
    begin
    select @fld=dbo.fn__str_at(@flds,'','',@i)
    select @sql=''select @r=count(*) from (select distinct [''+@fld+''] from [''+@tbl+'']) a''
    if @dbg=1 select @sql=@sql+'' print @r''
    select @r=null
    if @dbg=1 exec sp__printf @sql
    exec sp_executesql @sql,N''@r bigint out'',@r=@r out
    if @r=@nr
        begin
        if @pk is null select @pk=@fld
        exec sp__printf ''%s possible pk:%s'',@tbl,@fld
        select @pks=@pks+1
        end
    select @i=@i+1
    end

-- TODO:if @pks=0 ... search for combination
goto ret

help:
exec sp__usage @proc

ret:
return @ret
end -- sp__pkey'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__pkey:

-- ====================================================== sp__plan_calendar_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__plan_calendar_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121118
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__plan_calendar_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__plan_calendar_test') with nowait
        goto skip_sp__plan_calendar_test
        end
    if @ver>121118
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__plan_calendar_test') with nowait
        goto skip_sp__plan_calendar_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__plan_calendar_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__plan_calendar_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__plan_calendar_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,plan
    k:plan,calendar,test
    r:121118\s.zaglio: test plan calendar functions
*/
CREATE proc sp__plan_calendar_test
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    @days nvarchar(24), @i int,@dt datetime,
    @end_declare bit

-- =========================================================== initialization ==
select
    @i=1,
    @days='''',
    @end_declare=1

-- ======================================================== second params chk ==

-- =============================================================== #tbls init ==

-- ===================================================================== body ==
select
    cast(dbo.fn__plan_calendar(''2012-01-01'','''') as varbinary(48)) as [12-01-01],
    cast(dbo.fn__plan_calendar(''2012-31-01'','''') as varbinary(48)) as [12-31-01],
    cast(dbo.fn__plan_calendar(''2012-01-02'','''') as varbinary(48)) as [12-01-02],
    dt
from
    dbo.fn__plan_dates(2012,dbo.fn__plan_calendar(''2012-01-01'',''''))

while @i<13
    begin
    select
        -- 1st day of month
        @dt=''2012-01-''+right(''00''+cast(@i as varchar(2)),2),
        @days=dbo.fn__plan_calendar(@dt,@days)
        -- last day of month
    exec sp__printf ''dt=%s'',@dt
    select
        @dt=case @i
            when 12
            then ''2012-31-12''
            else convert(datetime,''2012-01-''+right(''00''+cast(@i+1 as varchar(2)),2))-1
            end,
        @days=dbo.fn__plan_calendar(@dt,@days),
        @i=@i+1
    exec sp__printf ''dt=%s'',@dt
    end

select cast(@days as varbinary(48)) days
select * from dbo.fn__plan_dates(2012,@days)

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
/*
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_me1:    select @e_msg=''write here msg''                              goto err
err_me2:    select @e_msg=''write this %s'',@e_p1=@var                    goto err
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test plan calendar functions

Parameters
    [param]     [desc]

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__plan_calendar_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__plan_calendar_test:

-- ============================================================= sp__print_table
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__print_table',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__print_table') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__print_table') with nowait
        goto skip_sp__print_table
        end
    if @ver>100919
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__print_table') with nowait
        goto skip_sp__print_table
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__print_table') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__print_table'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__print_table]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100919\s.zaglio: solved problem of print ''''
    v:100501\s.zaglio: added @reverse
    v:091229\s.zaglio: added @prefix
    v:091128\s.zaglio: a small optimization
    v:091127\s.zaglio: added @format to use s__printf instead of print
    v:091018\s.zaglio: will replace part or all sp__select. Used by sp__script,sp__copy
    t:
        create table #src (lno int identity(10,10),line nvarchar(4000))
        insert #src(line) select ''hello''
        insert #src(line) select null
        insert #src(line) select ''world''
        exec sp__print_table ''#src'' -- ,@dbg=1
        exec sp__print_table ''#src'',@prefix=''tst:''
        exec sp__print_table ''(select lno,line from #src)'',@dbg=1
        drop table #src
*/
CREATE proc [dbo].[sp__print_table]
    @tbl sysname =null,
    @autosize bit=0,
    @sizes sysname=null,     -- 20|10|5 chars
    @title sysname=null,
    @format bit=0,
    @prefix sysname=null,
    @reverse bit=0,
    @dbg bit=0
as
begin
set nocount on
declare
    @proc   sysname,
    @print  sysname,
    @sql    nvarchar(4000),@msg nvarchar(4000)

select
    @proc   =''sp__print_table''

if @tbl is null goto help
if @prefix is null
    select @print=case @format
        when 0 then ''if len(@line)=0 print char(13)+char(10) else print @line ''
        else ''exec sp__printf ''''%s'''',@line '' end
else
    begin
    select @prefix=replace(@prefix,'''''''','''''''''''')
    select @print=case @format
        when 0 then ''if len(@line)=0 print char(13)+char(10) else print ''''''+@prefix+''''''+@line ''
        else ''exec sp__printf ''''''+@prefix+''%s'''',@line '' end
    end

select @sql =''declare @line nvarchar(4000);''
            +''declare lines cursor local for select coalesce(line,'''''''') as line from ''+@tbl+'' tbl order by lno ''
            +case when @reverse=1 then ''desc;'' else '';'' end
            +''open lines;''
            +''while (1=1) ''
            +''begin ''
            +''fetch next from lines into @line ''
            +''if @@error != 0 or @@fetch_status != 0 break ''
            +@print
            +''end;''
            +''close lines deallocate lines;''
if not @title is null exec sp__printf @title
if @dbg=1 exec sp__printf @sql
exec(@sql)
goto ret
err_todo:
help:
select @msg=''NB. actually work only for tables (lno int,line nvarchar(max))''
exec sp__usage @proc,@msg
ret:
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__print_table:

-- ================================================================== sp__printf
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__printf',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130606
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__printf') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__printf') with nowait
        goto skip_sp__printf
        end
    if @ver>130606
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__printf') with nowait
        goto skip_sp__printf
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__printf') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__printf'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__printf]

begin try
exec dbo.sp_executesql @statement = N'/*  keep for MS compatibility
    l:see LICENSE file
    g:utility
    v:130606\s.zaglio: moved @force and @format_only into @opt
    v:130605\s.zaglio: added output of the result to @format,corrected help
    v:130424\s.zaglio: a small bug near leng>0 and resumed +1 of bug 130416
    v:130416\s.zaglio: a bug when called from printsql near crlf
    v:121010\s.zaglio: added {1}... markers
    v:110831\s.zaglio: a bug or misunderstanding near @format_only
    v:110705\s.zaglio: a bug near @tmp
    v:110607\s.zaglio: a bug near last \n
    v:110526\s.zaglio: a bug near last \n%s
    v:110518\s.zaglio: a bug near optimization
    v:110517\s.zaglio: a bug when more %s/d than supported
    v:110510\s.zaglio: escaped % in raiserror to exclude error 2787:Invalid format spec.
    v:110421\s.zaglio: used nvarchar(max) instead of ntext
    v:110314\s.zaglio: added split into 200 char''s lines
    v:110312\s.zaglio: a semiremake of sp__printf to allow >4000 chars (mssql2k compatible)
    t:sp__printf_test
*/
CREATE proc [dbo].[sp__printf]
    @format nvarchar(max)=null out,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null,
    @p5 sql_variant=null,
    @p6 sql_variant=null,
    @p7 sql_variant=null,
    @p8 sql_variant=null,
    @p9 sql_variant=null,
    @p0 sql_variant=null,
    @opt sysname=null,
    @dbg bit=null
    --,@err sysname=null    100106\s.zaglio: not so useful
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0
if not @opt is null select @opt=dbo.fn__Str_quote(@opt,''|'')

-- ============================================================== declaration ==

declare
    @icrlf bit,@sep nvarchar(2),
    @p int,@j int,@i1 int,
    @crlf nvarchar(2),@macro nvarchar(4000),
    @cr nchar(1),@lf nchar(1),
    @tmp nvarchar(4000),
    @nm bit,                -- new markers
    @p1s nvarchar(4000),
    @p2s nvarchar(4000),
    @p3s nvarchar(4000),
    @p4s nvarchar(4000),
    @p5s nvarchar(4000),
    @p6s nvarchar(4000),
    @p7s nvarchar(4000),
    @p8s nvarchar(4000),
    @p9s nvarchar(4000),
    @p0s nvarchar(4000)

declare @out table(lno int identity primary key,line nvarchar(4000))

select @icrlf=0 -- not include crlf into splittings

declare @lines table (pos int primary key,leng int)
declare
    @i int,@n int,@k int,@st sysname,
    @bs int, -- buffer size or the width of output
    @format_only bit, @force bit, @test bit

select
    @bs=200,@dbg=isnull(@dbg,0),@crlf=crlf,@cr=cr,@lf=lf,
    @force=1-isnull(charindex(''|print|'',@opt),0),
    @format_only=isnull(charindex(''|fo|'',@opt),0),
    @test=isnull(charindex(''|test|'',@opt),0)
from dbo.fn__sym()

if @format is null goto help

-- optimization for empty strings
if @format=''''
    begin
    if @test=0
        begin
        if @force=1 raiserror(@crlf,10,1) with nowait
        else print ''''
        end
    insert @out select ''''
    goto dispose
    end

if charindex(''{1}'',@format)>0
    begin
    select @nm=1
    select @p1s=isnull(convert(nvarchar(4000),@p1,126),''(null)'')
    select @p2s=isnull(convert(nvarchar(4000),@p2,126),''(null)'')
    select @p3s=isnull(convert(nvarchar(4000),@p3,126),''(null)'')
    select @p4s=isnull(convert(nvarchar(4000),@p4,126),''(null)'')
    select @p5s=isnull(convert(nvarchar(4000),@p5,126),''(null)'')
    select @p6s=isnull(convert(nvarchar(4000),@p6,126),''(null)'')
    select @p7s=isnull(convert(nvarchar(4000),@p7,126),''(null)'')
    select @p8s=isnull(convert(nvarchar(4000),@p8,126),''(null)'')
    select @p9s=isnull(convert(nvarchar(4000),@p9,126),''(null)'')
    select @p0s=isnull(convert(nvarchar(4000),@p0,126),''(null)'')
    end

-- optimization for one line
select @macro=substring(@format,1,@bs)
if datalength(@format)<=@bs
and charindex(@cr,@macro)=0 and charindex(''\n'',@macro)=0
    begin
    if @format_only=1
        begin
        if @force=1
            begin
            -- avoid error 2787 not catched by try
            select @tmp=replace(@macro,''%'',''%%'')
            -- raiserror (''test %s and %%s'',10,1) with nowait -- test (null) and (null)
            if @test=0 raiserror (@tmp,10,1) with nowait
            insert @out select @macro
            end
        else
            if @test=0 print @macro
        end
    else
        begin
        select @macro=replace(@macro,''\t'',char(9))
        select @macro=replace(@macro,''%t'',getdate())
        if @nm=1
            begin
            select @macro=replace(replace(@macro,''{1}'',@p1s),''{2}'',@p2s)
            select @macro=replace(replace(@macro,''{3}'',@p3s),''{4}'',@p4s)
            select @macro=replace(replace(@macro,''{5}'',@p5s),''{6}'',@p6s)
            select @macro=replace(replace(@macro,''{7}'',@p7s),''{8}'',@p8s)
            select @macro=replace(replace(@macro,''{9}'',@p9s),''{0}'',@p0s)
            end
        else
            begin
            select @macro=replace(@macro,''%d'',''%s'')
            select @macro=dbo.fn__printf(@macro,@p1,@p2,@p3,@p4,@p5,@p6,@p7,@p8,@p9,@p0)
            end
        if @force=1
            begin
            select @tmp=replace(@macro,''%'',''%%'')
            if @test=0 raiserror (@tmp,10,1) with nowait
            insert @out select @macro
            end
        else
            begin
            if @test=0 print @macro
            insert @out select @macro
            end
        end -- format
    goto dispose
    end -- less than @bs

declare @params table (id int identity(1,1), macro sql_variant)

-- ===================================================================== init ==

select @format_only=isnull(@format_only,0),@force=isnull(@force,1)

if @format_only=0
and not (
    @p1 is null and @p2 is null and @p3 is null and
    @p4 is null and @p5 is null and @p6 is null and
    @p7 is null and @p8 is null and @p9 is null and
    @p0 is null)
    begin
    insert @params(macro) select isnull(@p1,''(null)'')
    insert @params(macro) select isnull(@p2,''(null)'')
    insert @params(macro) select isnull(@p3,''(null)'')
    insert @params(macro) select isnull(@p4,''(null)'')
    insert @params(macro) select isnull(@p5,''(null)'')
    insert @params(macro) select isnull(@p6,''(null)'')
    insert @params(macro) select isnull(@p7,''(null)'')
    insert @params(macro) select isnull(@p8,''(null)'')
    insert @params(macro) select isnull(@p9,''(null)'')
    insert @params(macro) select isnull(@p0,''(null)'')
    end -- macros

-- ======================== identify row separator and create splitting table ==

select top 1 @i=charindex(@cr,@format),@j=charindex(@lf,@format)
if @i is null begin select @ret=-2 goto ret end

if @i>0 and @j=0 select @crlf=@cr
if @i=0 and @j>0 select @crlf=@lf
if @i=@j+1 select @crlf=@lf+@cr
-- else is @crlf

-- piece of code from sp__write_ntext_to_lines
select top 1 @i=1,@p=1,@j=1,@n=datalength(@format)/2
while 1=1
    begin
    select @i=charindex(@crlf,substring(@format,@j,4000))
    select @i1=charindex(''\n'',substring(@format,@j,4000))
    if @i1>0 and (@i1<@i or @i=0) select @i=@i1,@sep=''\n''
    else select @sep=@crlf
    if @i=0
        begin
        -- if @dbg=1 exec sp__printf ''j:%d, i:%d, @n:%d'',@j,@i,@n
        if @n>=@j/*+@lcrlf*/-1 insert @lines select @j,@n-@j+1
        break
        end

    -- if @dbg=1 exec sp__printf ''j:%d, i:%d'',@j,@i
    if @icrlf=1 or @i-len(@sep)+1<0
        insert @lines select @j,@i
    else
        insert @lines select @j,@i-len(@sep)+1-- 130416\s.zaglio

    select @j=@j+@i+len(@sep)-1
    end -- while

-- ============================================================== print lines ==

if @dbg=1 select * from @lines

select @i=1 -- macro index
declare cs cursor local for
    select leng,substring(@format,pos,leng) line
    from @lines
    -- where leng>0
    order by pos
open cs
while 1=1
    begin
    fetch next from cs into @k,@macro
    if @@fetch_status!=0 break

    if @macro=''''
        begin
        if @test=0 raiserror (@crlf,10,1) with nowait
        insert @out select ''''
        continue
        end

    if @format_only=0
        begin
        select @macro=replace(@macro,''\t'',char(9))
        select @macro=replace(@macro,''%t'',getdate())
        if @nm=1
            begin
            select @macro=replace(replace(@macro,''{1}'',@p1s),''{2}'',@p2s)
            select @macro=replace(replace(@macro,''{3}'',@p3s),''{4}'',@p4s)
            select @macro=replace(replace(@macro,''{5}'',@p5s),''{6}'',@p6s)
            select @macro=replace(replace(@macro,''{7}'',@p7s),''{8}'',@p8s)
            select @macro=replace(replace(@macro,''{9}'',@p9s),''{0}'',@p0s)
            end
        else
            begin
            select @macro=replace(@macro,''%d'',''%s'')

            select @i1=charindex(''%s'',@macro)
            while @i1>0
                begin
                if @dbg=1 raiserror(''macro=%s, i1=%d, i=%d'',10,1,@macro,@i1,@i)
                select top 1
                    @macro=  left(@macro,@i1-1)+
                             isnull(convert(nvarchar(4000),macro),''(null)'')+
                             substring(@macro,@i1+2,4000)
                from @params where id=@i
                select @i1=charindex(''%s'',@macro,@i1+2),@i=@i+1
                if @i>11 goto err_out
                end
            end -- macro replace
        end -- format_only

    if @force=1 and @format_only=0
        begin
        -- raiserror has a limit of 200-255 chars
        if @k<=@bs
            begin
            -- print @macro
            select @tmp=replace(@macro,''%'',''%%'')
            if @test=0 raiserror (@tmp,10,1) with nowait
            insert @out select @macro
            end
        else
            begin
            -- split into @bs char''s line
            while @k>@bs
                begin
                select @i1=@bs
                while (@i1>0 and
                       not substring(@macro,@i1,1) in ('' '',''='','':'',''|'','','','';'')
                      )
                    select @i1=@i1-1
                if @i1=0 select @i1=@bs
                select @tmp=substring(@macro,1,@i1)
                insert @out select @tmp
                if @format_only=0 select @tmp=replace(@tmp,''%'',''%%'')
                if @test=0 raiserror (@tmp,10,1) with nowait
                select @macro=substring(@macro,@i1+1,4000)
                select @k=len(@macro)
                end -- while k=len
            end
        end -- forse=1 and fmt_only=0
    else
        begin
        if @test=0 print @macro
        insert @out select @macro
        end

    end -- while of cursor
close cs
deallocate cs

select @ret=0

-- return printed strings to @format
dispose:
select @format = (
    (select line+@crlf
    from @out
    for xml path(''''),type).value(''.'',''nvarchar(max)'')
    )

goto ret

-- ===================================================================== errs ==

err_out:    exec sp__err ''too many %?'',@proc goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    print a formatted string using raiserror(10) to ensure
    immediate output to well track the code execution.
    raiserror has a limit of 200/240 chars so the sp__printf
    break longs string into chunks trying to respect spaces
    and punctuations

See also
    sp__printframe,sp__printsql

Parameters
    @format         uses same C definitions with some limits
                    %s  marker for a string
                    %d  marker for a number
                    %t  expandend into date-time
                    {1},{2},{...} are specifically replaced by @p1,@p2,...
                    return the formatted string
    @p1..@p0        replaces %s,%d,...
    @opt            options
                    fo      format only :do not apply %s,%d,... replacement
                    print   print instead of raiserror to force output;
                            print is faster because enqueue but the output
                            do not appear after the sql command;
                            raiserror flush immediatelly the output buffer;
                    test    do not output, used by sp__printf_test or to format

''
select @ret=-1

ret:
return @ret
end -- sp__printf'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__printf:

-- ============================================================= sp__printf_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__printf_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130725
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__printf_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__printf_test') with nowait
        goto skip_sp__printf_test
        end
    if @ver>130725
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__printf_test') with nowait
        goto skip_sp__printf_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__printf_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__printf_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__printf_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:sp__printf,test
    v:130725\s.zaglio: updated
    v:130607\s.zaglio: final ok message and managed option
    v:130606\s.zaglio: done
    r:130605\s.zaglio: test for sp__printf
    t:sp__printf_test @opt=''run'',@dbg=1
    t:sp__printf_test @opt=''run|managed'',@dbg=1
*/
CREATE proc sp__printf_test
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @id int,@tst nvarchar(max),@rst nvarchar(max),@otst nvarchar(max),
    @p1 sql_variant,@p2 sql_variant,@p3 sql_variant,@p4 sql_variant,
    @run bit,@ok bit,@v nvarchar(max),@i int,@crlf nvarchar(2),
    @n int,@nok int,@managed bit,@d datetime

declare @test table (
    id int,
    tst nvarchar(max),
    p1 sql_variant null,
    p2 sql_variant null,
    p3 sql_variant null,
    p4 sql_variant null,
    opt sysname null,
    rst nvarchar(max)
    )

-- =========================================================== initialization ==
select
    @nok=0, @n=0,
    @crlf=crlf,
    @run=isnull(charindex(''|run|'',@opt),0),
    @managed=isnull(charindex(''|managed|'',@opt),0),
    @d=getdate()
from fn__sym()

-- ======================================================== second params chk ==
-- =============================================================== #tbls init ==

-- ===================================================================== body ==

insert @test(id,tst,rst)
select 10,
''simple test'',
''simple test''

insert @test(id,tst,rst)
select 20,
''line one\nline two'',
''line one
line two''

insert @test(id,tst,rst)
select 30,
''line one\n\nline two\n'',
''line one

line two
''

insert @test(id,tst,rst)
select 40,
'''',
''''

insert @test(id,tst,p1,p2,p3,rst)
select 50,
''today:%t\np1=%s\np2=%d\np3=%s'',1,''two'',3,
''today:%
p1=1
p2=two
p3=3''

insert @test(id,tst,opt,rst)
select 60,''test %s format %d only %t'',''fo'',
''test %s format %d only %t''

insert @test(id,tst,p1,p2,rst)
select 70,''%s\n%s'',''test1'',''test2'',
''test1
test2''

insert @test(id,tst,p1,p2,p3,rst)
select 80,''test {1}\ntest {2}'',''t1'',''t2'',''t3'',
''test t1
test t2''

insert @test(id,tst,p1,p2,rst)
select 84,''test (%s,%s)'',''%drop%'',''%drop%'',
''test (%drop%,%drop%)''

insert @test(id,tst,p1,rst)
select 85,''hex test %x'',-1,
''hex test 0xffffffff''

insert @test(id,tst,p1,rst)
select 86,''hex test %x'',123123123123123,
''hex test 0x0f000001b37304d6fa6f0000''

insert @test(id,tst,p1,p2,p3,p4,rst)
select 87,''%s%s%s%s'',''test('','''',12,'')'',
''test(12)''

insert @test(id,tst,p1,p2,p3,rst)
select 88,''%%%s%%%s%%%s%%'',''test('','''','')'',
''%test(%%)%''

-- mega test
select @i=10000,@v=''start\n''
while (@i>0) select @v=@v+''01234567890\n'',@i=@i-1
select @v=@v+''end''
insert @test(id,tst,rst)
select 90,@v,replace(@v,''\n'',@crlf)

-- test with lines of more than 200 chars
select @v=''start\n''
select @i=20
while (@i>0) select @v=@v+''0123456789;'',@i=@i-1
select @i=20
while (@i>0) select @v=@v+''01234567890'',@i=@i-1
select @v=@v+''\nend''
insert @test(id,tst,rst)
select 100,@v,
''start
0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;0123456789;
0123456789;0123456789;
01234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001
end''

if @run=0 goto help

-- sp__printf_test @dbg=2

-- the output of printf add virtually the last crlf of the out of console
update @test set rst=rst+@crlf
-- ================================================================ run tests ==

declare cs cursor local for
    select id,tst,p1,p2,p3,p4,opt,rst from @test
open cs
while 1=1
    begin
    fetch next from cs into @id,@otst,@p1,@p2,@p3,@p4,@opt,@rst
    if @@fetch_status!=0 break

    select @n=@n+1

    if @dbg>0 exec sp__prints @id
    select @tst=@otst

    select @opt=case when @managed=1 or @dbg=0 then ''test|'' else '''' end
               +isnull(@opt,'''')
    exec sp__printf @tst out,@p1,@p2,@p3,@p4,@opt=@opt

    select @ok=0
    if @tst is null
        begin
        select @err_msg=''error in sp__printf for %s''
        raiserror(@err_msg,11,1,@otst)
        if @ret=0 exec @ret=sp__err @err_msg,@opt=''noerr''
        end

    if charindex(''%t'',@otst)>0 and charindex(''|fo'',@opt)=0
        begin
        if @dbg>2 exec sp__printf ''-- tst like''
        if @tst like @rst select @ok=1
        end
    else
        begin
        if @dbg>2 exec sp__printf ''-- tst eq''
        if @tst=@rst select @ok=1
        end

    if @ok=0
        begin
        select @err_msg=''^^ failed test %d''
        select @ret=@id
        if @managed=1 break
        raiserror(@err_msg,11,1,@id)
        if @dbg>0
            begin
            select @tst=replace(replace(@tst,char(13),''\r''),char(10),''\n''),
                   @rst=replace(replace(@rst,char(13),''\r''),char(10),''\n'')
            raiserror(''^^ test %d:"%s" failed with:"%s"'',11,1,@id,@tst,@rst)
            end
        end
    else
        select @nok=@nok+1

    end -- cursor cs
close cs
deallocate cs

if @dbg>0 exec sp__prints ''end''

if @managed=0 exec sp__elapsed @d,''%d/%d tests passed'',@v1=@nok,@v2=@n

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    check know formats of sp__printf

Parameters
    @opt        options
                run     run tests
                managed when used by caller, stop at 1st error and return the id
    @dbg        1=show passages info
                3=show dbg info

''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end -- proc sp__printf_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__printf_test:

-- ============================================================== sp__printframe
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__printframe',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130605
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__printframe') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__printframe') with nowait
        goto skip_sp__printframe
        end
    if @ver>130605
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__printframe') with nowait
        goto skip_sp__printframe
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__printframe') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__printframe'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__printframe]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    todo: manage @level
    v:130605\s.zaglio: removed sp__printf parameters
    v:110701\s.zaglio: added @out
    v:110624\s.zaglio: added multiline comment
    v:100723\s.zaglio: added code style
    v:100626\s.zaglio: print a well visible comment also in code style
    t:sp__printframe ''this is a test''
    t:sp__printframe ''this is a code comment'',#
    t:
        create table #src(lno int identity,line nvarchar(4000))
        exec sp__printframe ''#src test'',@out=''#src''
        select * from #src
        drop table #src
*/
CREATE proc sp__printframe
    @format nvarchar(4000)=null,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null,
    @level smallint=null,
    @out sysname   =null
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0
if @format is null goto help

declare
    @r nvarchar(4000),@l1 nvarchar(240),@l2 nvarchar(240),@l int,
    @crlf nvarchar(2),@l3 nvarchar(240), @l4 nvarchar(240)

-- select @crlf=crlf from fn__sym()

if @out is null
    begin

    -- multiline comment
    if charindex(''%s'',@format)=0 and charindex(''%d'',@format)=0
    and not @p1 is null
        begin
        exec sp__printf ''''
        exec sp__printf ''-- ##########################'' -- col 30
        exec sp__printf ''-- ##''
        select @r=''-- ## ''+@format
        if not @p1 is null select @l1=''-- ## ''+convert(nvarchar(240),@p1)
        if not @p2 is null select @l2=''-- ## ''+convert(nvarchar(240),@p2)
        if not @p3 is null select @l3=''-- ## ''+convert(nvarchar(240),@p3)
        if not @p4 is null select @l4=''-- ## ''+convert(nvarchar(240),@p4)
        exec sp__printf @r
        if not @l1 is null exec sp__printf @l1
        if not @l2 is null exec sp__printf @l2
        if not @l3 is null exec sp__printf @l3
        if not @l4 is null exec sp__printf @l4
        exec sp__printf ''-- ##''
        exec sp__printf ''-- ########################################################'' -- col 60
        goto ret
        end

    if coalesce(@p1,'''')!=''#''
        begin
        select @format=''-- ## ''+@format
        select @r=dbo.fn__printf(@format,@p1,@p2,@p3,@p4,null,null,null,null,null,null)
        exec sp__printf ''''
        exec sp__printf ''-- ##########################'' -- col 30
        exec sp__printf ''-- ##''
        exec sp__printf @r
        exec sp__printf ''-- ##''
        exec sp__printf ''-- ########################################################'' -- col 60
        end
    else
        begin
        -- sp__printframe ''this is a code comment'',#

        select @r=''exec sp__printframe ''''''+
                  dbo.fn__printf(@format,@p1,@p2,@p3,@p4,null,null,null,null,null,null)+
                  '''''' -- ##''
        select @l=len(@r)
        select @l1=''-- ''+replicate(''#'',@l-3)
        select @l2=''-- ################''+replicate('' '',@l-21)+''##''

        exec sp__printf ''''
        exec sp__printf @l1
        exec sp__printf @l2
        raiserror(@r,10,1)
        exec sp__printf @l2
        exec sp__printf @l1
        end
    end -- @out is null
else
    begin
    -- out to table
    if @out!=''#src'' or object_id(''tempdb..#src'') is null goto err_out

    -- multiline comment
    if charindex(''%s'',@format)=0 and charindex(''%d'',@format)=0
    and not @p1 is null
        begin
        insert #src(line) select ''-- ##########################'' -- col 30
        insert #src(line) select ''-- ##''
        select @r=''-- ## ''+@format
        if not @p1 is null select @l1=''-- ## ''+convert(nvarchar(240),@p1)
        if not @p2 is null select @l2=''-- ## ''+convert(nvarchar(240),@p2)
        if not @p3 is null select @l3=''-- ## ''+convert(nvarchar(240),@p3)
        if not @p4 is null select @l4=''-- ## ''+convert(nvarchar(240),@p4)
        insert #src(line) select @r
        if not @l1 is null insert #src(line) select @l1
        if not @l2 is null insert #src(line) select @l2
        if not @l3 is null insert #src(line) select @l3
        if not @l4 is null insert #src(line) select @l4
        insert #src(line) select ''-- ##''
        insert #src(line) select ''-- ########################################################'' -- col 60
        goto ret
        end

    if coalesce(@p1,'''')!=''#''
        begin
        select @format=''-- ## ''+@format
        select @r=dbo.fn__printf(@format,@p1,@p2,@p3,@p4,null,null,null,null,null,null)
        insert #src(line) select ''''
        insert #src(line) select ''-- ##########################'' -- col 30
        insert #src(line) select ''-- ##''
        insert #src(line) select @r
        insert #src(line) select ''-- ##''
        insert #src(line) select ''-- ########################################################'' -- col 60
        end
    else
        begin
        -- sp__printframe ''this is a code comment'',#

        select @r=''exec sp__printframe ''''''+
                  dbo.fn__printf(@format,@p1,@p2,@p3,@p4,null,null,null,null,null,null)+
                  '''''' -- ##''
        select @l=len(@r)
        select @l1=''-- ''+replicate(''#'',@l-3)
        select @l2=''-- ################''+replicate('' '',@l-21)+''##''

        insert #src(line) select ''''
        insert #src(line) select @l1
        insert #src(line) select @l2
        insert #src(line) select @r
        insert #src(line) select @l2
        insert #src(line) select @l1
        end

    end

goto ret

-- =================================================================== errors ==

err_out:    exec @ret=sp__err ''Only #src is admitted or %s not found'',@proc,@p1=@out

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    print a well visible comment also in code style

Parameters
    @format     same of sp__printf (%s,%d,%t are markers for @p1,@p2...)
    @p1         if @p1 is ''''#'''', the title is printed in code style
                to integrate into code

Examples
''
exec sp__printf ''\n    sp__printframe ''''this is a test''''               -- produces:''
exec sp__printframe ''this is a test''
exec sp__printf ''\n    sp__printframe ''''this is a code comment'''',#     -- produces:''
exec sp__printframe ''this is a code comment'',#

select @ret=-1
ret:
return @ret
end -- sp__printframe'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__printframe:

-- ================================================================== sp__prints
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__prints',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130612.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__prints') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__prints') with nowait
        goto skip_sp__prints
        end
    if @ver>130612.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__prints') with nowait
        goto skip_sp__prints
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__prints') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__prints'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__prints]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130612.1000\s.zaglio: addapted to fn__prints
    v:130611\s.zaglio: extended 8< macro
    v:130107\s.zaglio: added 8< macro comment
    v:120921\s.zaglio: added @p1,...
    v:110329\s.zaglio: added use of |
    v:100723\s.zaglio: print a separator for better code reading
    t:sp__prints ''errors|help|8<|8<test|8<long test''
*/
CREATE proc sp__prints
    @comment nvarchar(4000)=null,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null
as
begin
set nocount on
declare @proc sysname,  @ret int
select @proc=object_name(@@procid), @ret=0

if @comment is null goto help

declare @line nvarchar(80)

if left(@comment,2)=''8<''
    begin
    select @line=dbo.fn__prints(@comment,@p1,@p2,@p3,@p4)
    raiserror(@line,10,1)
    goto ret
    end

declare cs cursor local for
    select token
    from fn__str_table(@comment,''|'')
    where 1=1
open cs
while 1=1
    begin
    fetch next from cs into @comment
    if @@fetch_status!=0 break

    select @line=dbo.fn__prints(@comment,@p1,@p2,@p3,@p4)
    raiserror(@line,10,1)

    end -- while of cursor
close cs
deallocate cs

goto ret

help:
-- sp__prints ''max len of comment is 76''
exec sp__usage @proc,''
Scope
    print a separator for better code read

Parameters
    @comment    the comment to use as separator
                - can use multiple comments, separated by |
                - accept macro 8< to print scissor line
    @p1,...     replaces %s, %d as in printf

Examples
    sp__prints ''''max len of comment is 76''''
-- ================================================= max len of comment is 76 ==
''

select @ret=-1
ret:
return @ret
end -- proc sp__prints'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__prints:

-- ================================================================ sp__printsql
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__printsql',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140203
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__printsql') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__printsql') with nowait
        goto skip_sp__printsql
        end
    if @ver>140203
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__printsql') with nowait
        goto skip_sp__printsql
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__printsql') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__printsql'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__printsql]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:140203\s.zaglio: bug near some end lines
    v:130729\s.zaglio: bug near %
    v:130530\s.zaglio: complete
    r:130529\s.zaglio: removed #src, using splitter, added @opt
    v:110518\s.zaglio: added more @sql...
    v:110315\s.zaglio: print sql code refactored
    t:
        declare @sql nvarchar(max)
        select @sql=''''''''+replace(definition,'''''''','''''''''''')+''''''''
        from sys.sql_modules
        where object_id=object_id(''sp__printsql'')
        exec sp__printsql @sql
*/
CREATE proc sp__printsql
    @sql1 ntext = null,
    @sql2 ntext = null,
    @sql3 ntext = null,
    @sql4 ntext = null,
    @opt  sysname = 0,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
-- ========================================================= param formal chk ==
if @sql1 is null and @sql2 is null and @sql3 is null and @sql4 is null goto help

-- ============================================================== declaration ==
declare
    @sql nvarchar(max),@line nvarchar(4000),@crlf nvarchar(2),
    @i int,@j int,@n int,@lcrlf tinyint
-- =========================================================== initialization ==
select
    @sql=cast(@sql1 as nvarchar(max))
        +isnull(@crlf+cast(@sql2 as nvarchar(max)),'''')
        +isnull(@crlf+cast(@sql3 as nvarchar(max)),'''')
        +isnull(@crlf+cast(@sql4 as nvarchar(max)),''''),
    @crlf=crlf,
    @lcrlf=len(crlf)
from fn__sym()
-- ======================================================== second params chk ==
-- ===================================================================== body ==
-- if substring(@sql1,1,4)=''#src'' exec sp__print_table ''#src''


select @n=len(@sql),@i=1,@j=charindex(@crlf,@sql,@i)
while 1=1
    begin
    -- exec sp__printf ''i=%d, j=%d'',@i,@j
    if @j=0 select @j=@n+1
    select @line=replace(substring(@sql,@i,@j-@i),''%'',''%%''),@j=@j+@lcrlf,@i=@j
    raiserror(@line,10,1)
    if @j>@n break
    select @j=charindex(@crlf,@sql,@i)
    end

goto ret

-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    print long sql code eventually refactored
    (TODO: today this is only a marker for future full functional sp)

Parameters
    @sql1..4    sql code
    @opt        options (not used)

''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__printsql'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__printsql:

-- =============================================================== sp__proc_stop
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__proc_stop',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091026
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__proc_stop') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__proc_stop') with nowait
        goto skip_sp__proc_stop
        end
    if @ver>091026
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__proc_stop') with nowait
        goto skip_sp__proc_stop
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__proc_stop') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__proc_stop'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__proc_stop]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    v:091026\s.zaglio: stop or show how to breack run of code without close connection
    c:this is only to not loose too much time to read,understand,verify the manual
    g:utility
*/
CREATE proc sp__proc_stop @proc sysname=null,@msg sysname=null,@severity int=17
as
begin
if @proc is null or @msg is null goto help
raiserror(''%s:%s'',@severity,1,@proc,@msg)
help:
exec sp__usage ''sp__proc_stop''
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__proc_stop:

-- ================================================================= sp__profile
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__profile',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130630
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__profile') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__profile') with nowait
        goto skip_sp__profile
        end
    if @ver>130630
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__profile') with nowait
        goto skip_sp__profile
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__profile') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__profile'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__profile]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    d:130630\s.zaglio: sp__util_profile
    v:130630\s.zaglio: refactor
    v:101130\s.zaglio: Self adjusting trace to capture worst performing TSQL
    c:http://sqlserverperformance.idera.com/tsql-optimization/finding-heaviest-tsql-optimize-sql-server/
    t:sp__profile 2,@dbg=1
    t:xp_cmdshell ''dir f:\sqldata\*.*''
*/
CREATE proc sp__profile
    @MaxTraceTimeInMinutes smallint = null,
    @MinTraceTimeInMinutes smallint = null,
    @dbg smallint = 0
as
begin
set nocount on
declare @proc sysname, @ret int
select @proc=object_name(@@procid),@ret=0

if nullif(@MaxTraceTimeInMinutes,0) is null goto help

select @MinTraceTimeInMinutes=isnull(@MinTraceTimeInMinutes,
                                     @MaxTraceTimeInMinutes*0.75)

exec sp__printf ''Will run for %d minutes, please do not stop'',
                @MaxTraceTimeInMinutes

set transaction isolation level read uncommitted
set lock_timeout 20000
set implicit_transactions off
if @@trancount > 0
        commit transaction
set language us_english
set cursor_close_on_commit off
set query_governor_cost_limit 0
set numeric_roundabort off

declare @spid int, @databaseid int, @applicationname nvarchar(256),
        @tridl int, @trids int,
        @trstatusl int, @trstatuss int,
        @tracepathl nvarchar(1024), @tracepaths nvarchar(1024),
        @tracetablelong nvarchar(1024), @tracetableshort nvarchar(1024),
        @currentduration bigint, @oldduration bigint,
        @tracerowsfound int, @maxtracefilesize bigint,
        @laststmtendtime datetime, @lastsampletime datetime,
        @laststmtendtimes datetime, @lastsampletimes datetime,
        @secsincelastcollection int, @bypasscollection bit,
        @shorttraceexists bit, @estimated bit, @bitone bit,
        @gatherwaittimeinsecs tinyint,
        @nooftsqlstmtsperminhighwater int, @nooftsqlstmtsperminlowwater int,
        @mindurationforlongtrace bigint, @mindurationforshorttrace bigint,
        @starttracetime datetime, @uniquetsql int,
        @statisticaldeviation dec(14,4), @statisticallastaverageduration bigint,
        @statisticalaverageduration bigint, @statisticalsamplesize int, @statisticalminimumpopulation int,
        @execstring varchar(50),
        @textdata nvarchar(4000),
        @duration bigint, @endtime datetime,
        @reads bigint, @writes bigint, @cpu int,
        @entrydatebucket int, @multiplierthisrow int, @multiplier int,
        @position int, @asciiposition int,
        @tsqlhashcode bigint,
        @multilineskip tinyint, @singlelineskip tinyint,
        @twocharacter char(2), @onecharacterascii int,
        @previoushighduration bigint

select @spid = @@spid,
        @currentduration = 5000000,
        @maxtracefilesize = 25, -- in mb
        @nooftsqlstmtsperminhighwater = 500, --- (per minute) this number for testing, normally would be perhaps 200
        @nooftsqlstmtsperminlowwater = 150, --- (per minute) this number for testing, normally would be perhaps 30
        @mindurationforlongtrace = 80000, -- overlap point between high and low traces (in microseconds)
        @mindurationforshorttrace = 1, -- in minutes
        @starttracetime = getdate(),
        @uniquetsql = 0,
        @statisticaldeviation = 100,
        @statisticalsamplesize = 30,
        @statisticalminimumpopulation = 300,
        @bitone = 1

--the io path to tempdb on high-end systems will be san, hence we should put the trace data files there too rather than on a local drive
-- use tempdb
select @tracepathl = left(physical_name, len(physical_name) -
charindex(''\'',reverse(physical_name))) + ''\''
        from sys.database_files
        where file_id = 1
select @tracepaths = @tracepathl + ''heaviestshorttrace''
select @tracepathl = @tracepathl + ''heaviestlongtrace''

if @dbg>1
    begin
    -- exec sp_trace_setstatus 2, 0 -- stop trace
    -- exec sp_trace_setstatus 3, 2 -- close trace
    select ''exec sp_trace_setstatus ''+convert(nvarchar(2),traceid)+'', 2 -- close the long trace'' sql,*
    -- select *
    from sys.fn_trace_getinfo(0)
    where property = 2 and convert(nvarchar(1024),value) like ''%\heaviest%trace%''
    select [name] from tempdb..sysobjects where [name] like ''##%''
    end

gather:

exec sp__printf ''%t gathering...'',''%t''

select @bypasscollection = 0,
        @laststmtendtime  = getdate(),
        @lastsampletime = getdate(),
        @laststmtendtimes = getdate(),
        @lastsampletimes = getdate(),
        @gatherwaittimeinsecs = 30

if @dbg>1
    select traceid,value,@tracepaths tps,@tracepathl tpl
    from sys.fn_trace_getinfo(0)
    where property=2

select @trids = traceid
        from sys.fn_trace_getinfo(0)
        where property = 2 and convert(nvarchar(1024),value) like @tracepaths+''%''
if @@rowcount <> 0
        select @shorttraceexists = 1
else
        select @shorttraceexists = 0

select @tridl = traceid
-- select *
        from sys.fn_trace_getinfo(0)
        where property = 2 and convert(nvarchar(1024),value) like @tracepathl+''%''
if @@rowcount <> 0
        goto longtraceexists

createlongtrace:


select @bypasscollection = 1

if @dbg=1
    begin
    if @tridl is null exec sp__printf ''+--creating long trace''
    else exec sp__printf ''+--reopening long trace''
    end
exec sp_trace_create @tridl output, 2, @tracepathl, @maxtracefilesize, null, 10
if @dbg=1 exec sp__printf ''  +--long trace %d in %s'',@tridl,@tracepathl
if @shorttraceexists = 0
    begin
    if @dbg=1
        begin
        if @tridl is null exec sp__printf ''+--creating short trace''
        else exec sp__printf ''+--reopening short trace''
        end
    exec sp_trace_create @trids output, 2, @tracepaths, @maxtracefilesize, null, 10
    if @dbg=1 exec sp__printf ''  +--short trace %d in %s'',@trids,@tracepaths
    end



--select trace columns to show for completed statements on long trace
exec sp_trace_setevent @tridl, 41, 1, @bitone -- textdata
exec sp_trace_setevent @tridl, 41, 3, @bitone -- dbid
exec sp_trace_setevent @tridl, 41, 10, @bitone -- applicationname
exec sp_trace_setevent @tridl, 41, 12, @bitone --spid
exec sp_trace_setevent @tridl, 41, 13, @bitone -- duration in microseconds
exec sp_trace_setevent @tridl, 41, 15, @bitone -- endtime
exec sp_trace_setevent @tridl, 41, 16, @bitone --diskreadslogical
exec sp_trace_setevent @tridl, 41, 17, @bitone --diskwritesphysical
exec sp_trace_setevent @tridl, 41, 18, @bitone --cpu time

--select trace columns to show for completed sp statements on long trace
exec sp_trace_setevent @tridl, 45, 1, @bitone -- textdata
exec sp_trace_setevent @tridl, 45, 3, @bitone -- dbid
exec sp_trace_setevent @tridl, 45, 10, @bitone -- applicationname
exec sp_trace_setevent @tridl, 45, 12, @bitone --spid
exec sp_trace_setevent @tridl, 45, 13, @bitone -- duration in microseconds
exec sp_trace_setevent @tridl, 45, 15, @bitone -- endtime
exec sp_trace_setevent @tridl, 45, 16, @bitone --diskreadslogical
exec sp_trace_setevent @tridl, 45, 17, @bitone --diskwritesphysical
exec sp_trace_setevent @tridl, 45, 18, @bitone --cpu time

if @shorttraceexists = 0
        begin
        --select trace columns to show for completed statements on short trace
        exec sp_trace_setevent @trids, 41, 1, @bitone -- textdata
        exec sp_trace_setevent @trids, 41, 3, @bitone -- dbid
        exec sp_trace_setevent @trids, 41, 10, @bitone -- applicationname
        exec sp_trace_setevent @trids, 41, 12, @bitone --spid
        exec sp_trace_setevent @trids, 41, 13, @bitone -- duration in microseconds
        exec sp_trace_setevent @trids, 41, 15, @bitone -- endtime
        exec sp_trace_setevent @trids, 41, 16, @bitone --diskreadslogical
        exec sp_trace_setevent @trids, 41, 17, @bitone --diskwritesphysical
        exec sp_trace_setevent @trids, 41, 18, @bitone --cpu time
        --select trace columns to show for completed sp statements on short trace
        exec sp_trace_setevent @trids, 45, 1, @bitone -- textdata
        exec sp_trace_setevent @trids, 45, 3, @bitone -- dbid
        exec sp_trace_setevent @trids, 45, 10, @bitone -- applicationname
        exec sp_trace_setevent @trids, 45, 12, @bitone --spid
        exec sp_trace_setevent @trids, 45, 13, @bitone -- duration in microseconds
        exec sp_trace_setevent @trids, 45, 15, @bitone -- endtime
        exec sp_trace_setevent @trids, 45, 16, @bitone --diskreadslogical
        exec sp_trace_setevent @trids, 45, 17, @bitone --diskwritesphysical
        exec sp_trace_setevent @trids, 45, 18, @bitone --cpu time
        end

--set filters
exec sp_trace_setfilter @tridl, 13, 0, 4, @currentduration  -- set duration of long trace >=
exec sp_trace_setfilter @tridl, 12, 0, 1, @spid  -- dont trace this spid''s actions
if @shorttraceexists = 0
        begin
        exec sp_trace_setfilter @trids, 13, 0, 3, @mindurationforlongtrace -- set duration of short trace < long trace
        exec sp_trace_setfilter @trids, 13, 0, 4, @mindurationforshorttrace -- set duration of short trace >=1
        exec sp_trace_setfilter @trids, 12, 0, 1, @spid  -- dont trace this spid''s actions
        end

longtraceexists:
if  (select count(*) from tempdb..sysobjects where name like ''%heaviesttracesave%'') <> 0
        begin
        select @laststmtendtime = laststmtendtime,
                @lastsampletime = lastsampletime,
                @laststmtendtimes = laststmtendtimes,
                @lastsampletimes = lastsampletimes
                from ##heaviesttracesave
        end
else
        begin
        create table ##heaviesttracesave(laststmtendtime datetime, lastsampletime datetime, laststmtendtimes datetime, lastsampletimes datetime)
        insert into ##heaviesttracesave values (getdate(), getdate(), getdate(), getdate())
        end

if @bypasscollection = 1
        goto bypasscollection

-- process deep infrequent "short" trace
exec sp_trace_setstatus @trids, 1  -- start the short trace
waitfor delay ''00:00:01'' -- give the short trace exactly a second to run
exec sp_trace_setstatus @trids, 0 -- stop the short trace

-- trace file processing and consolidation starts here

if  (select count(*) from tempdb..sysobjects where name like ''%heaviesttraceusage%'') = 0
        create table ##heaviesttraceusage(
                [tsqlhashcode] [bigint] not null,
                [databaseid] [int] not null,
                [noofruns] [int] not null,
                [estimated] [bit] not null,
                [totcpu] [bigint] not null,
                [totio] [bigint] not null,
                [totduration] [bigint] not null,
                [highduration] [bigint] not null,
                [applicationname] [nvarchar] (256) null,
                [tsqltext] [nvarchar](4000) not null,
        constraint [pk_traceusage] primary key clustered
        (       [tsqlhashcode] asc, [databaseid] asc))
--      with (data_compression = page) sql 2008 only

select @multiplier = datediff(second, @lastsampletimes, getdate())
if @multiplier < 1
        select @multiplier = 1

select @tracerowsfound = 0

select top 1 @tracetablelong=convert(nvarchar(512),value)
        from sys.fn_trace_getinfo(0)
        where property = 2 and traceid=@tridl
select top 1 @tracetableshort=convert(nvarchar(512),value)
        from sys.fn_trace_getinfo(0)
        where property = 2 and traceid=@trids

-- select @tracetablelong = @tracepathl + ''.trc'',
--        @tracetableshort = @tracepaths + ''.trc''

declare traceoutput cursor fast_forward for
        select 0, textdata, databaseid, applicationname, duration, endtime, reads, writes, cpu
                from fn_trace_gettable(@tracetablelong, default)
                where duration > @mindurationforlongtrace and
                        endtime > @laststmtendtime and
                        textdata is not null
        union all
        select 1, textdata, databaseid, applicationname, duration, endtime, reads, writes, cpu
                from fn_trace_gettable(@tracetableshort, default)
                where duration > 0 and
                        endtime > @laststmtendtimes and
                        textdata is not null

open traceoutput

fetch next from traceoutput into @estimated, @textdata, @databaseid, @applicationname, @duration, @endtime, @reads, @writes, @cpu

while @@fetch_status = 0
        begin
        -- i have used a simple checksum hash for the tsqlhashcode col after stripping off the parameters
        -- but the actual tsql is saved in the tracetsql table with parameters for easy evaluation
        select @textdata = ltrim(rtrim(@textdata)), @position = 1, @asciiposition = 1, @tsqlhashcode = 0, @multilineskip = 0, @singlelineskip = 0
        while @position <= len(@textdata)
                begin
                select @twocharacter = substring(@textdata, @position, 2)
                if @twocharacter = ''/*'' collate sql_latin1_general_cp1_ci_as and @singlelineskip = 0
                        begin
                        set @multilineskip = 1
                        set @position = @position + 2
                        continue
                        end
                if @twocharacter = ''*/'' collate sql_latin1_general_cp1_ci_as and @singlelineskip = 0
                        begin
                        set @multilineskip = 0
                        set @position = @position + 2
                        continue
                        end
                if @twocharacter = ''--'' collate sql_latin1_general_cp1_ci_as and @multilineskip = 0
                        begin
                        set @singlelineskip = 1
                        set @position = @position + 2
                        continue
                        end
                select @onecharacterascii = ascii(substring(upper(@twocharacter), 1, 1))
                if (@onecharacterascii between 10 and 13) and @multilineskip = 0
                        begin
                        set @singlelineskip = 0
                        set @position = @position + 1
                        continue
                        end
                if @multilineskip = 0 and @singlelineskip = 0 and @onecharacterascii <> 32
                        select @tsqlhashcode = @tsqlhashcode + (@onecharacterascii * @asciiposition)
                set @position = @position + 1
                if @onecharacterascii <> 32
                        set @asciiposition = @asciiposition + 1
                end
        if @estimated = 0
                begin
                select @multiplierthisrow = 1
                select @tracerowsfound = @tracerowsfound + 1
                if @endtime > @laststmtendtime
                        select @laststmtendtime = @endtime
                end
        else
                select @multiplierthisrow = @multiplier
        if @endtime > @laststmtendtimes
                select @laststmtendtimes = @endtime
        select @previoushighduration = highduration
                from ##heaviesttraceusage
                where tsqlhashcode = @tsqlhashcode and
                        databaseid = @databaseid
        if @@rowcount = 0
                begin
                insert into ##heaviesttraceusage values (@tsqlhashcode, @databaseid, @multiplierthisrow, @estimated, (@cpu * @multiplierthisrow), ((@reads + @writes) * @multiplierthisrow), (@duration * @multiplierthisrow), @duration, @applicationname, @textdata)
                select @uniquetsql = @uniquetsql + 1
                end
        else
                if @duration > @previoushighduration
                        begin
                        update ##heaviesttraceusage
                                set noofruns = ##heaviesttraceusage.noofruns + @multiplierthisrow,
                                        totcpu = totcpu + (@cpu * @multiplierthisrow),
                                        totio = totio + ((@reads + @writes) * @multiplierthisrow),
                                        totduration = totduration + (@duration * @multiplierthisrow),
                                        highduration = @duration,
                                        tsqltext = @textdata
                                where tsqlhashcode = @tsqlhashcode and
                                        databaseid = @databaseid
                        end
                else
                        update ##heaviesttraceusage
                                set noofruns = ##heaviesttraceusage.noofruns + @multiplierthisrow,
                                        totcpu = totcpu + (@cpu * @multiplierthisrow),
                                        totio = totio + ((@reads + @writes) * @multiplierthisrow),
                                        totduration = totduration + (@duration * @multiplierthisrow)
                                where tsqlhashcode = @tsqlhashcode  and
                                        databaseid = @databaseid

        fetch next from traceoutput into @estimated, @textdata, @databaseid, @applicationname, @duration, @endtime, @reads, @writes, @cpu
        end

close traceoutput
deallocate traceoutput

update ##heaviesttracesave
        set laststmtendtime = @laststmtendtime,
                lastsampletime = getdate(),
                laststmtendtimes = @laststmtendtimes,
                lastsampletimes = getdate()

if @dbg=1 exec sp__printf ''get current duration from long trace''
select @currentduration = convert(int,value)
        -- select * from fn_trace_getfilterinfo(0)
        from fn_trace_getfilterinfo(@tridl)
        where columnid = 13 ;  -- get current duration

select @secsincelastcollection = datediff (ss, @lastsampletime, getdate())
if (@tracerowsfound / (@secsincelastcollection / 60.00) > @nooftsqlstmtsperminhighwater) or
        ((@tracerowsfound / (@secsincelastcollection / 60.00) < @nooftsqlstmtsperminlowwater) and
        (@currentduration > @mindurationforlongtrace)) --too many or too few rows collected, adjust filter
        begin
        if @dbg=1 exec sp__printf ''stop and close long trace %d'',@tridl
        exec sp_trace_setstatus @tridl, 0 -- stop the trace
        exec sp_trace_setstatus @tridl, 2 -- close the trace
        select @oldduration = @currentduration
        if @tracerowsfound / (@secsincelastcollection / 60.00) > @nooftsqlstmtsperminhighwater
                begin
                select @gatherwaittimeinsecs = @gatherwaittimeinsecs / 5
                select @currentduration = (((@tracerowsfound / (@secsincelastcollection / 60.00)) / @nooftsqlstmtsperminhighwater) * 0.25 * @currentduration) + @currentduration
                end
        else
                begin
                select @gatherwaittimeinsecs = @gatherwaittimeinsecs / 4 -- should be 3
                select @currentduration = @currentduration / 1.5
                if @currentduration < @mindurationforlongtrace
                        select @currentduration = @mindurationforlongtrace
                end
        goto createlongtrace
        end

bypasscollection:
if @dbg=1 exec sp__printf ''restart long trace %d'',@tridl
exec sp_trace_setstatus @tridl, 1 -- re-start the trace as it may have been stopped manually or automatically

select @execstring = ''waitfor delay ''''00:00:'' + convert(varchar(2), @gatherwaittimeinsecs) + ''''''''
exec (@execstring)
---derive statistical deviation
if  (select count(*) from tempdb..sysobjects where name like ''%heaviesttraceusage%'') <> 0
        select @statisticalaverageduration = avg(totduration/noofruns)
                from ##heaviesttraceusage
                where tsqlhashcode in (select top (@statisticalsamplesize) with ties tsqlhashcode
                                                                                        from ##heaviesttraceusage
                                                                                        order by totduration/noofruns desc)
if @statisticallastaverageduration is not null
        select @statisticaldeviation = abs(1 - ((@statisticalaverageduration * 1.0000) / (@statisticallastaverageduration * 1.0000)))
        select @statisticallastaverageduration = @statisticalaverageduration

if (datediff(minute,@starttracetime, getdate()) >= @mintracetimeinminutes) and
        ((datediff(minute,@starttracetime, getdate()) >= @maxtracetimeinminutes) or
        ((@uniquetsql > @statisticalminimumpopulation) and (@statisticaldeviation < 0.03))) -- less than 3% deviation
        goto exitroutine
goto gather

exitroutine:
if @dbg=1 exec sp__printf ''stop and close traces %d and %d'',@tridl,@trids

exec sp_trace_setstatus @tridl, 0 -- stop the long trace
exec sp_trace_setstatus @tridl, 2 -- close the long trace
exec sp_trace_setstatus @trids, 0 -- stop the short trace
exec sp_trace_setstatus @trids, 2 -- close the short trace

if  (select count(*) from tempdb..sysobjects where name like ''%heaviesttracesave%'') <> 0
        drop table ##heaviesttracesave

-- give statistical confidence indicator as a percentage
select 100-(@statisticaldeviation*100) as [confidence %]

-- show heaviest tsql (all runs)
select noofruns as [number of executions],
                totcpu,
                totio,
                totduration as [total duration (microseconds)],
                highduration as [longest duration (microseconds)],
                totduration/noofruns as [ave duration (microseconds)],
                db_name(databaseid) as [database],
                tsqltext,
                applicationname
        from ##heaviesttraceusage
        order by 4 desc

drop table ##heaviesttraceusage
goto ret
-- =================================================================== errors ==
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    Two level - Self adjusting trace to capture worst performing
    TSQL statements using a sampling technique
    Output is stored in a table which is finally output once statistical
    confidence is high enough or sample time expires

Notes
    - originally from http://sqlserverperformance.idera.com/tsql-optimization/finding-heaviest-tsql-optimize-sql-server/

Parameters
    @MaxTraceTimeInMinutes  routine may exit sooner if enough samples
                            have been gathered (normally 20 minutes)
    @MinTraceTimeInMinutes  (optional) minimum time to run even if statistics
                            have "settled" (normally 75%:15 minutes)

Examples
    exec sp__profile 20

''
select @ret=-1
-- ===================================================================== exit ==

ret:
return @ret
end -- sp__profile'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__profile:

-- ============================================================= sp__profile_sql
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__profile_sql',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130630
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__profile_sql') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__profile_sql') with nowait
        goto skip_sp__profile_sql
        end
    if @ver>130630
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__profile_sql') with nowait
        goto skip_sp__profile_sql
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__profile_sql') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__profile_sql'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__profile_sql]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,nomssql2k
    d:130630\s.zaglio: sp__util_sqltrace
    v:130630\s.zaglio: renamed and small refactor
    v:100810\s.zaglio: originally from http://www.sommarskog.se/index.html
    t:
        exec sp__profile_sql ''
            exec sp__printf ''''test''''
            select top 1000 * from sysobjects
            ''
*/
CREATE  procedure sp__profile_sql
  @batch nvarchar(max) = null,      -- sql batch to analyse
  @minreads bigint = 1,             -- min reads (logical)
  @mincpu int = 0,                  -- min cpu time (milliseconds)
  @minduration bigint = 0,          -- min duration (microseconds)
  @factor varchar(50) = ''duration'', -- % (duration, reads, writes, cpu)
  @order varchar(50) = '''',          -- order (duration, reads, writes, cpu)
  @plans varchar(50) = '''',          -- include query plans - intentive (actual, estimated)
  @rollback bit = 0,                -- run in a transaction and rollback
  @timeout int = 300                -- set a maximum trace duration (seconds)
as
begin
set nocount on;
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

if @batch is null goto help

declare
    @id int, @spid int, @file nvarchar(256), @fsize bigint, @plan int, @on bit, @rc int,
    @stoptime datetime, @total int, @rccpu int, @rcduration int

select
    @spid = @@spid, @on = 1, @fsize = 5,
    @plan = case lower(@plans) when ''actual'' then 146 when ''estimated'' then 122 end,
    @stoptime = dateadd(second, @timeout, getdate())

exec sp__get_temp_dir @file out
select @file = @file+''\''+cast(newid() as char(36))

exec sp_trace_create @id output, 2, @file, @fsize, @stoptime

if @plan is not null begin
    exec sp_trace_setevent @id, @plan, 1, @on   -- xml plan
    exec sp_trace_setevent @id, @plan, 5, @on   -- xml plan / line
    exec sp_trace_setevent @id, @plan, 34, @on   -- xml plan / objectname
    exec sp_trace_setevent @id, @plan, 51, @on   -- xml plan / eventsequence
end
exec sp_trace_setevent @id, 45, 51, @on   -- sp:stmtcompleted / eventseq
exec sp_trace_setevent @id, 41, 51, @on   -- sql:stmtcompleted / eventseq
exec sp_trace_setevent @id, 166, 51, @on   -- sql:stmtrecompile / eventseq
exec sp_trace_setevent @id, 166, 21, @on   -- sql:stmtrecompile / subclass
exec sp_trace_setevent @id, 45, 1, @on   -- sp:stmtcompleted / textdata
exec sp_trace_setevent @id, 41, 1, @on   -- sql:stmtcompleted / textdata
exec sp_trace_setevent @id, 166, 1, @on   -- sql:stmtrecompile / textdata
exec sp_trace_setevent @id, 45, 13, @on   -- sp:stmtcompleted / duration
exec sp_trace_setevent @id, 41, 13, @on   -- sql:stmtcompleted / durantion
exec sp_trace_setevent @id, 45, 16, @on   -- sp:stmtcompleted / reads
exec sp_trace_setevent @id, 41, 16, @on   -- sql:stmtcompleted / reads
exec sp_trace_setevent @id, 45, 17, @on   -- sp:stmtcompleted / writes
exec sp_trace_setevent @id, 41, 17, @on   -- sql:stmtcompleted / writes
exec sp_trace_setevent @id, 45, 18, @on   -- sp:stmtcompleted / cpu
exec sp_trace_setevent @id, 41, 18, @on   -- sql:stmtcompleted / cpu
exec sp_trace_setevent @id, 45, 5, @on   -- sp:stmtcompleted / line
exec sp_trace_setevent @id, 41, 5, @on   -- sql:stmtcompleted / line
exec sp_trace_setevent @id, 45, 34, @on   -- sp:stmtcompleted / objectname
exec sp_trace_setevent @id, 45, 29, @on   -- sp:stmtcompleted / nestlevel

exec sp_trace_setfilter @id, 12, 0, 0, @spid -- spid = @@spid
exec sp_trace_setfilter @id, 13, 0, 4, @minduration -- duration >= @min
exec sp_trace_setfilter @id, 16, 0, 4, @minreads -- reads >= @minreads
exec sp_trace_setfilter @id, 18, 0, 4, @mincpu -- cpu >= @mincpu

if @rollback=1 begin tran

exec sp_trace_setstatus @id, 1
exec (@batch)
exec sp_trace_setstatus @id, 0

if @@trancount>0 rollback

exec sp_trace_setstatus @id, 2

declare @results table (
    eventclass smallint,
    subclass smallint,
    textdata nvarchar(4000),
    objectname varchar(128),
    nesting smallint,
    linenumber smallint,
    duration numeric(18,3),
    reads int,
    cpu int,
    writes int,
    compile int,
    rccpu int,
    rcduration bigint,
    xplan xml,
    id bigint primary key
)

-- load trace
select @file = @file+''.trc''
insert @results
select eventclass, eventsubclass, textdata, objectname, nestlevel-2,
linenumber, duration/1000.0, reads, cpu, writes, 0, null, null, '''', eventsequence
from fn_trace_gettable ( @file , default )
where eventsequence is not null

-- sequence query plans
if @plan is not null
update m set
  xplan = s.textdata
from @results s
cross apply(
  select top 1 * from @results
  where id > s.id and linenumber=s.linenumber and objectname=s.objectname
  order by id) m
where s.eventclass = @plan

-- sequence recompiles
update m set
compile = 1,
subclass = s.subclass,
rccpu = m.xplan.value(''*[1]/*[1]/*[1]/*[1]/*[1]/*[1]/@compilecpu'',''int''),
rcduration = m.xplan.value(''*[1]/*[1]/*[1]/*[1]/*[1]/*[1]/@compiletime'',''int'')
from @results s
cross apply(
select top 1 * from @results
where id > s.id and textdata=s.textdata
order by id) m
where s.eventclass = 166

-- remove xplan variables
update @results set xplan.modify(''delete *[1]/*[1]/*[1]/*[1]/*[1]/*[1]/@compiletime'')
where xplan is not null
update @results set xplan.modify(''delete *[1]/*[1]/*[1]/*[1]/*[1]/*[1]/@compilecpu'')
where xplan is not null

-- total measure
select @total = nullif(max(case lower(@factor) when ''cpu'' then cpu
when ''reads'' then reads
when ''writes'' then writes
else duration end),0),
@rcduration = sum(rcduration),
@rccpu = sum(rccpu),
@rc = sum(case when eventclass=166 then 1 else 0 end)
from @results

update @results set
rcduration = @rcduration,
rccpu = @rccpu,
compile = @rc
where objectname=''sqltrace''

-- results
select case when objectname=''sqltrace'' then '''' else
isnull(cast(nullif(floor((@total/2+100*sum(case lower(@factor) when ''cpu'' then cpu+isnull(rccpu,0)
  when ''reads'' then reads
  when ''writes'' then writes
  else duration+isnull(rcduration,0) end))/@total),0) as varchar)+''%'','''') end as factor,
case when textdata like ''exec%'' then ''\---- ''+textdata
  when textdata like ''%statman%'' then ''statistics -- ''+textdata
  else textdata end as text,
case when objectname=''sqltrace'' or count(*)=1 then '''' else cast(count(*) as varchar) end as calls,
case when objectname=''sqltrace'' then '''' else cast(nesting as varchar) end as nesting,
case when objectname=''sqltrace'' then '''' else objectname+'' - ''+cast(linenumber as varchar) end [object - line],
sum(duration) as duration,
isnull(cast(nullif(sum(cpu),0) as varchar),'''') as cpu,
isnull(cast(nullif(sum(reads),0) as varchar),'''') as reads,
isnull(cast(nullif(sum(writes),0) as varchar),'''') as writes,
isnull(cast(nullif(sum(compile),0) as varchar),'''') as compiles,
case subclass
  when 1 then ''local'' when 2 then ''stats'' when 3 then ''dnr''
  when 4 then ''set'' when 5 then ''temp'' when 6 then ''remote''
  when 7 then ''browse'' when 8 then ''qn'' when 9 then ''mpi''
  when 10 then ''cursor'' when 11 then ''manual'' else '''' end reason,
case when sum(compile)>0 then isnull(cast(sum(rcduration) as varchar),''?'') else '''' end as rcduration,
case when sum(compile)>0 then isnull(cast(sum(rccpu) as varchar),''?'') else '''' end as rccpu,
cast(cast(xplan as nvarchar(4000)) as xml) xplan
from @results
where eventclass in (41,45)
group by nesting, objectname, linenumber, textdata, eventclass, subclass, cast(xplan as nvarchar(4000))
order by min(case when @order='''' then id end),
sum(case lower(@order) when ''cpu'' then cpu+isnull(rccpu,0)
   when ''reads'' then reads
   when ''writes'' then writes
   else duration+isnull(rcduration,0) end) desc
goto ret

help:
exec sp__usage @proc,''
Scope
    show execution info about code passed into @batch

Notes
    - originally from http://www.sommarskog.se/index.html

Parameters
    @batch          sql batch to analyse
    @minreads       min reads (logical)
    @mincpu         min cpu time (milliseconds)
    @minduration    min duration (microseconds)
    @factor         % (duration(default), reads, writes, cpu)
    @order          order (duration, reads, writes, cpu)
    @plans          include query plans - intentive (actual, estimated)
    @rollback       run in a transaction and rollback
    @timeout        set a maximum trace duration (seconds)

Result Set
==========
Name            Description
--------------- --------------------------------------------------------------------
Factor          The total percent of the chosen measure taken by this statement.
                See the parameter @factor above. Blank if the percentage is 0.
                If the total of @factor is 0, Factor will be blank for all columns.
Text            The text of the statement
Calls           Number of times this statement was executed if more than 1.
Nesting         On which nesting level the statement was executed.
Object - Line   SQL module and line number for the statement.
Duration        Duration for the statement in milliseconds with three decimals.
                See also the section An Issue with Durations
                in (http://www.sommarskog.se/sqlutil/sqltrace.html)
Reads           Number of reads for the statement according to the trace. Blank when 0.
Writes          Number of writes for the statement according to the trace. Blank when 0.
CPU             CPU time for the statement in milliseconds. Blank when when 0.
Compiles        Number of recompiles for the statement if any.
Reason          Reason why the statement was recompiled. See the table below for the meaning of the codes.
rcDuration      Time spent on recompiling the statement. This value is derived from the execution plan,
                and thus only populated if you supply a value for @plans. The value is in milliseconds.
                The value for the batch itself – which has sqltrace as the object –
                has the total recompilation time for the batch.
rcCPU           CPU time for the recompilation of the statement. Like rcDuration, only populated
                if you included execution plans in the trace.
XPlan           The execution plan for the statement. Blank if you did not specify a value for @plans.

Recompilation Reasons
=====================
Code    SubClassNo  Description
Local   1           Schema changed.
Stats   2           Statistics changed.
DNR     3           Deferred compile.
SET     4           SET option changed.
Temp    5           Temp table changed.
Remote  6           Remote rowset changed.
Browse  7           FOR BROWSE permissions changed.
QN      8           Query notification environment changed.
MPI     9           Partitioned view changed.
Cursor 10           Cursor option changed.
Manual 11           Option(RECOMPILE) or WITH RECOMPILE requested.
''

ret:
return @ret
end -- sp__util_sqltrace'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__profile_sql:

-- =============================================================== sp__recompile
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__recompile',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090811
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__recompile') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__recompile') with nowait
        goto skip_sp__recompile
        end
    if @ver>090811
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__recompile') with nowait
        goto skip_sp__recompile
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__recompile') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__recompile'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__recompile]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090811\s.zaglio: removed apply of alter on #src''
    v:090801\s.zaglio: exapanded to use #src and sp__script_alter
    v:090720\s.zaglio: replaced ##tmp... with #src for multi thread
    v:090705\s.zaglio: added drop of tmp table
    v:090623\s.zaglio: recompile a nested/connected object
    t:sp__recompile ''fn_mat_all'',@dbg=1
    t:sp__recompile ''fn_mat_all'',@dbg=1,@svr=''gamon'',@db=''ramses'',@uid=''sa'',@pwd='''',@tofile=1
    t:
        create table #src (lno int identity(10,10),line nvarchar(4000))
        set nocount on
        insert into #src(line) select ''create table test_sp_recompile (''
        insert into #src(line) select ''         id int''
        insert into #src(line) select ''         )''
        exec sp__recompile ''#src'',@dbg=1,@srv=''gamon'',@db=''ramses'',@uid=''sa'',@pwd='''',@tofile=1
        select * from test_sp_recompile
        drop table test_sp_recompile
        drop table #src
*/
CREATE proc [dbo].[sp__recompile]
    @obj sysname=null,      -- if #src use this (without alter)
    @srv sysname=null,
    @db sysname=null,
    @uid sysname=null,
    @pwd sysname=null,
    @tofile bit=0,
    @dbg bit=0
as
begin
set nocount on
declare
    @r int,
    @t datetime,
    @msg nvarchar(4000),
    @sql nvarchar(4000),
    @sql1 nvarchar(4000),
    @sql2 nvarchar(4000),
    @sql3 nvarchar(4000),
    @line nvarchar(4000),
    @l int,@i int,@j int,@n int,@tmp sysname,@alter nvarchar(4000),
    @crlf nvarchar(2), @step smallint,
    @file sysname,
    @maxscp int

select
    @r=0,
    @crlf=char(13)+char(10),@step=10,@tmp=''#src'',
    @sql='''',@sql1='''',@sql2='''',@sql3=''''

if @obj is null goto help

if @obj!=@tmp
    begin
    create table #src (lno int identity(10,10),line nvarchar(4000))
    exec sp__script @obj,@out=@tmp,@step=@step -- ,@dbg=@dbg

    -- calculate size of script
    exec sp__script_size @i out,@msize=@maxscp out,@go=0   -- because alter want the ''go''
    if @i>@maxscp select @tofile=1
    -- change create [...] with alter [...]
    exec sp__script_alter @alter=1,@step=@step,@dbg=0
    end

exec sp__script_reduce @normalize=8 -- remove while line on top and bottom

if @tofile=1
    begin
    set @file=''%temp%\tmp_''+replace(convert(nvarchar(48),newid()),''-'',''_'')+''.sql''
    end

-- load script and compile it
if @tofile=0
    exec sp__script_cache @sql out,@sql1 out,@sql2 out,@sql3 out,@go=0
else    -- to file
    begin
    if @dbg=1 print ''output to file:''+coalesce(@file,''(null)'')
    exec sp__file_write @file out,@table=''#src'',@addcrlf=1,@dbg=@dbg
    end

if @dbg=1 and @tofile=0 exec sp__script ''#src''
if @dbg=1 exec sp__elapsed @t out,''recompiling at:''
if @tofile=0 and @dbg=1 print ''(memory compiling)''
if @tofile=0 exec sp__script_run
if @tofile=1
    begin
    if @srv is null or @db is null or @uid is null or @pwd is null goto err_tofile
    declare @cmd nvarchar(4000)
    set @cmd=''osql -S''+@srv+'' -d''+@db+'' -U''+@uid+'' -P''+@pwd+'' -i''+@file+'' -n''
    if @dbg=1 print @cmd
    create table #cmdout (lno int identity,line nvarchar(4000))
    insert into #cmdout
    exec master..xp_cmdshell @cmd
    if @dbg=1 select * from #cmdout
    exec sp__drop @file  -- sp__usage ''sp__select''
    if exists(select * from #cmdout)
        begin
        select @r=1
        select @msg=''#!''+coalesce(line,'''') from #cmdout where lno=1
        select @msg=@msg+@crlf+coalesce(line,'''') from #cmdout where lno=2
        select @msg=@msg+@crlf+coalesce(line,'''') from #cmdout where lno=3
        select @msg=@msg+@crlf+coalesce(line,'''') from #cmdout where lno=4
        if @msg=''#!'' select @msg=null,@r=0
        end
    end -- to file
if @dbg=1 exec sp__elapsed @t out,''compiled in ms:''

if @obj!=@tmp
    exec sp__drop @tmp

goto ret

err_nocreate:   select @r=-1,@msg=''#!create keyword not found'' goto ret
err_type:       select @r=-2,@msg=''#!unk type'' goto ret
err_con:        select @r=-3,@msg=''#!no concurrency sp or error previous error'' goto ret
err_src:        select @r=-4,@msg=''#!source type not known'' goto ret
err_tofile:     select @r=-5,@msg=''#!login info mandatory'' goto ret

help:
select @msg =''parameters:\n''
            +''\tcan be #src passed by caller formatted as "lno_line"\n''
            +''\tcan be a name of a table formatted as "lno_line"\n''
            +''\tcan be a file\n''
            +''The format "lno_line" is lno int,line nvarchar(4000)\n''
exec sp__usage ''sp__recompile'',@extra=@msg
select @msg=null

ret:
if not @msg is null exec sp__printf @msg
return @r
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__recompile:

-- =============================================================== sp__reg_write
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__reg_write',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__reg_write') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__reg_write') with nowait
        goto skip_sp__reg_write
        end
    if @ver>100919.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__reg_write') with nowait
        goto skip_sp__reg_write
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__reg_write') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__reg_write'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__reg_write]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100919.1000\s.zaglio:write/delete a windows registry
    t:
        exec sp__reg_write ''hklm\software\sp__reg\test'',''wtest'',@dbg=1
        exec sp__reg_write ''hklm\software\sp__reg\test1'',3,@dbg=1
        exec sp__reg_write ''hklm\software\sp__reg\test2'',2.3,@dbg=1

        exec sp__reg_write ''hklm\software\sp__reg\test'',null,@dbg=1
        exec sp__reg_write ''hklm\software\sp__reg'',null,@dbg=1
        exec sp__reg_write ''hklm\software\sp__reg\%'',null,@dbg=1
*/
CREATE proc sp__reg_write
    @key nvarchar(512)  = null,
    @val sql_variant    = null out,
    @dbg int=0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname,  @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=''sp__reg_write'', @ret=0

if @key is null goto help

-- declarations
create table #stdout (KeyExists int null)
create table #values (val sysname null, data sql_variant null)
declare @exists bit,@exists_parent bit,
        @k nvarchar(512),@v sysname,@t sysname,
        @r sysname,@vs nvarchar(4000),@vn int,@vb varbinary(4000)

select @t=convert(sysname,sql_variant_property(@val,''BaseType''))
select @t=case @t
          when ''tinyint''    then ''reg_dword''
          when ''int''        then ''reg_dword''
          when ''smallint''   then ''reg_dword''
          when ''bigint''     then ''reg_sz''
          when ''nvarchar''   then ''reg_sz''
          when ''varchar''    then ''reg_sz''
          when ''varbinary''  then ''reg_binary''
          when ''binary''     then ''reg_binary''
          else ''reg_sz''
          end

if @t=''reg_sz''      select @vs=convert(nvarchar(4000),@val)
if @t=''reg_dword''   select @vn=convert(int,@val)
if @t=''reg_binary''  select @vb=convert(varbinary(4000),@val)

-- initialization
select @r=[root],@k=[key],@v=[value] from dbo.fn__reg_parse(@key)
select @key=@k+''\''+@v

-- ===================================================================== body ==
insert #stdout exec master..xp_regread @r,@k
select top 1 @exists_parent=KeyExists from #stdout

if @exists_parent=1
    begin
    insert #values exec master..xp_regenumvalues @r,@k
    if @dbg=1 select * from #values
    select @exists=isnull((select top 1 1 from #values where val like @v),0)
    end
else
    select @exists=0

if @dbg=1 exec sp__printf ''exists: "%s":%d; "%s":%d'',@key,@exists,@k,@exists_parent

if @val is null
    begin
    if @dbg=1 exec sp__printf ''delete from %s val %s\%s or key %s'',@r,@k,@v,@key
    if @exists=1 and @v!=''%'' exec master..xp_regdeletevalue @r,@k,@v
    else
        begin
        if @v=''%'' and @exists_parent=1 exec master..xp_regdeletekey @r,@k
        else goto err_nokey
        end
    end
else
    begin
    if @t=''reg_sz''      exec master..xp_regwrite @r,@k,@v,@t,@vs
    if @t=''reg_dword''   exec master..xp_regwrite @r,@k,@v,@t,@vn
    if @t=''reg_binary''  exec master..xp_regwrite @r,@k,@v,@t,@vb
    if @dbg=1 exec sp__printf ''write reg %s\\%s\%s of type "%s" with "%s"'',@r,@k,@v,@t,@val
    end

goto ret

-- =================================================================== errors ==
err_nokey:  select @ret=1   goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    write/delete a registry of windows

Parameters
    @key    name of key with key_value; key_value can be % for delete of the key
    @val    value (will be converted into string or int or bigint)
            if NULL, delete the key; if return NULL, the key do not exists
    return  -1 for help, 0 if ok, 1 if key/val not exists

Examples
    -- delete the key
    sp__reg_write ''''hklm\software\microsoft\windows nt\currentversion\aedebug\debugger'''',null

    -- read the key value and store it in @val. If @val is null the key do not exists
    sp__reg_read ''''hklm\software\microsoft\windows nt\currentversion\aedebug\debugger'''',@val out
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__reg_write'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__reg_write:

-- ================================================================= sp__restore
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__restore',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131014.2300
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__restore') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__restore') with nowait
        goto skip_sp__restore
        end
    if @ver>131014.2300
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__restore') with nowait
        goto skip_sp__restore
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__restore') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__restore'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__restore]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131014.2300\s.zaglio: a small bug under mssql2k12
    v:120726\s.zaglio: add run option and more info
    v:110325\s.zaglio: a modern version
    v:090808\S.Zaglio: added @replace
    v:081229\S.Zaglio: added @err out
    v:081227\S.Zaglio: added nounload,recovery options
    v:081130\S.Zaglio: added @rename to allow fast dublicate of db
    v:081125\S.Zaglio: added mssql2008 compatibility and corrected a bug
    v:081114\S.Zaglio: added comment and mssql2005 compatibility
    v:080509\S.Zaglio: added @simul
    v:080508\S.Zaglio: restore db from file replacing dst file name. Don''t manage multi filegroup.
    c:for more advanced versione see http://www.sqlservercentral.com/scripts/Backup+%2F+Restore/32003/
    c:copy this on master db of dest server for first restore
    t:sp__restore ''db'',''device.bak''
    t:sp__backup ''%temp%'',@opt=''doit'',@dbg=1  -- backup this db
    t:sp__restore ''test'',''C:\DOCUME~1\NETWOR~1\IMPOST~1\Temp\utility_110325_1251.bak''
*/
CREATE proc [dbo].[sp__restore]
    @db         sysname=null,
    @device     nvarchar(1024)=null out,
    @opt        sysname=null,
    @dbg        int=0
as
begin
set nocount on

declare @proc sysname,@ret int
select  @proc=object_name(@@procid),@ret=0,
        @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @db is null goto help
if @db=db_name() goto err_db

-- ============================================================== declaration ==

declare
    @mssqlver smallint,@dbid int,@crlf nvarchar(2),@doit bit,
    @sql nvarchar(4000),@tmp nvarchar(1024)

-- ===================================================================== init ==

select
    @crlf=crlf,
    @doit=case
          when charindex(''|doit|'',@opt)>0 then 1
          when charindex(''|run|'',@opt)>0  then 1
          else 0
          end
from fn__sym()

exec sp__get_temp_dir @tmp out
select @device=replace(@device,''%temp%'',@tmp)

if @dbg=1 exec sp__printf ''dev=%s, tmp=%s, db=%s, dt=%s'',@device, @tmp,@db

/* to enable sp__restore to work on mssql2005/2008
EXECUTE sp_configure ''show advanced options'', 1 RECONFIGURE WITH OVERRIDE
EXECUTE sp_configure ''xp_cmdshell'', ''1'' RECONFIGURE WITH OVERRIDE
EXECUTE sp_configure ''Ole Automation Procedures'', ''1'' RECONFIGURE WITH OVERRIDE
EXECUTE sp_configure ''SMO and DMO XPs'', ''1'' RECONFIGURE WITH OVERRIDE
EXECUTE sp_configure ''show advanced options'', 0 RECONFIGURE WITH OVERRIDE
*/

if not exists(
    select [name]
    from master..sysdatabases
    where [name]=@db
    )
    begin
    if @doit=1 exec(''create database [''+@db+'']'')
    else goto err_mdb
    end

select @mssqlver=convert(smallint,substring(@@version,22,5))
if not @mssqlver in (2000,2005,2008,2012) goto err_ver

-- drop database @name_db    the dest db must not exist

select @dbid=[dbid]
from master.dbo.sysdatabases
where [name]=@db

if (select count(*)
    from master.dbo.sysaltfiles
    where [dbid]=@dbid
   )>2 goto err_max

-- restore must replace original files with current files

-- destination paths
/*
select @logical_name = null
select @logical_name=rtrim(filename)
from master.dbo.sysaltfiles
where [dbid]=@dbid and fileid=1

select @file_path_data=reverse(@logical_name)
select @i=charindex(''\'',@file_path_data)
select @file_path_data=substring(@logical_name,1,len(@logical_name)-@i+1)
select @logical_name=rtrim(filename)
from master.dbo.sysaltfiles
where dbid=@dbid and fileid=2

select @file_log=reverse(@logical_name)
select @i=charindex(''\'',@file_log)
select @file_log=substring(@logical_name,1,len(@logical_name)-@i+1)
*/

create table #Media (
    LogicalName nvarchar(128) NULL,
    PhysicalName nvarchar(260) NULL,
    [Type] nchar(1) NULL,
    FileGroupName nvarchar(128) NULL,
    [Size] numeric(20,0) NULL,
    [MaxSize] numeric(20,0) NULL
    )
if @mssqlver>=2005
    alter table #Media add
    FileID bigint NULL,
    CreateLSN numeric(25,0) NULL,
    DropLSN numeric(25,0) NULL,
    UniqueID uniqueidentifier NULL,
    ReadOnlyLSN numeric(25,0) NULL,
    ReadWriteLSN numeric(25,0) NULL,
    BackupSizeInBytes bigint NULL,
    SourceBlockSize int NULL,
    FileGroupID int NULL,
    LogGroupGUID uniqueidentifier NULL,
    DifferentialBaseLSN numeric(25,0) NULL,
    DifferentialBaseGUID uniqueidentifier NULL,
    IsReadOnly bit NULL,
    IsPresent bit NULL

if @mssqlver>=2008
    alter table #Media add TDEThumbprint varbinary(32) NULL;

-- load info from backup file
insert into #media
exec(''RESTORE FILELISTONLY FROM DISK = ''''''+@device+'''''''')

alter table #media add
    LocalName sysname,
    LocalPath nvarchar(1024)

/*
    select case when saf.status=2 then ''D'' else ''L'' end as t,*
    from master.dbo.sysaltfiles saf where dbid=db_id(''utility'')
*/

update #media set
    LocalName=saf.name,
    LocalPath=saf.filename
from #media m
join master.dbo.sysaltfiles saf
on  saf.dbid=@dbid and m.type=case when saf.status=2 then ''D'' else ''L'' end

if @@error!=0 goto ret

if @dbg=1
    select
        [Type],LogicalName,PhysicalName,
        LocalName,LocalPath
    from #media

select @sql=''
    restore database [%db%]
    from disk = ''''%device%'''' with file=1, nounload,stats=10,replace
    ,recovery
''
exec sp__str_replace @sql out,''%db%|%device%'',@db,@device
select @sql=@sql+''    ,move ''''''+isnull(LogicalName,''?LogicalName?'')
                +'''''' to ''''''+isnull(LocalPath,''?LocalPath?'')+''''''''+@crlf
from #media

if @doit=1
    begin
    exec(@sql)
    exec sp__prints ''-- xp_cmdshell ''''del "%s"'''''',@device
    end
else
    exec sp__printsql @sql
if @@error!=0
    begin
    if @dbg=1 exec sp__printsql @sql
    select @ret=-2
    goto ret
    end

/*
set @net_uid=coalesce(@net_uid,'''') set @net_pwd=coalesce(@net_pwd,'''')
if @simul=0 print @sql else print ''''
if @simul=0 begin
    exec(@sql)
    set @err=0 set @err=@@error if @err!=0 goto ret
end
else print ''*** SIMULATION *** (use @simul=0 to run really)''
if @net_uid<>'''' or @net_pwd<>'''' begin
    set @i=charindex(''\\'',@device)
    if @i<>0 set @i=@i+2
    set @i=charindex(''\'',@device,@i)
    set @cmd=''net use ''+substring(@device,1,@i-1)+'' ''+@net_pwd+'' /user:''+@net_uid
    exec master..xp_cmdshell @cmd,no_output
end
--    se il db @name_db non esiste, allora esci dalla procedura
*/

goto ret

-- =================================================================== errors ==
err_mdb:    exec @ret=sp__err ''missing db; use CREATE DATABASE [%s]'',@proc,@p1=@db
            goto ret
err_db:     exec @ret=sp__err ''cannot restore on current db itself'',@proc
            goto ret
err_ver:    exec @ret=sp__err ''Tested only with MSSql 2K,2K5,2K8 (this is %d)'',@proc
            goto ret
err_max:    exec @ret=sp__err ''This procedure manage only db of 1 data file and 1 logical file'',@proc
            goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    restore a db from a device.
    The Divice can be a remote file. (not yet implemented)
    If begin with \\, can add usr/pwd separated by |.

Parameters
    @db     the name of database to owerwrite
    @device the backup source
            if %temp%, uses the autogenerated temp file name
    @opt    options
            run         modern version of old "doit"
            doit        execute instead of print code

Examples
    sp__restore ''''testdb'''',''''%temp%\mybackup.bak''''
''
ret:
return @ret
end -- [sp__restore]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__restore:

-- =================================================================== sp__ruler
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__ruler',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100917
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__ruler') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__ruler') with nowait
        goto skip_sp__ruler
        end
    if @ver>100917
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__ruler') with nowait
        goto skip_sp__ruler
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__ruler') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__ruler'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__ruler]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100917\s.zaglio: print a rules for text imports
    t:sp__ruler 120
*/
create proc sp__ruler
    @len    int=0,
    @start  int=1
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname,  @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=''sp__ruler'', @ret=0

if @len is null or @len<11 or @len>4000 goto help

declare @r1 nvarchar(4000),@r2 nvarchar(4000)
select @r1=''-- '',@r2=''-- ''
while (@start<=@len)
    begin
    select @r2=@r2+case when @start%10=0 then ''.'' else convert(nchar(1),@start%10) end
    if @start%10=0 select @r1=@r1+right(''          ''+convert(nvarchar(4),@start),10)
    select @start=@start+1
    end -- while

print @r1 print @r2
goto ret

help:
exec sp__usage @proc,''
Scope
    print a ruler of len @len for text imports

Example

--         10        20
-- 12345678901234567890
''
select @ret=-1

ret:
return @ret
end -- sp__ruler'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__ruler:

-- ================================================================= sp__run_cmd
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__run_cmd',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091127
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__run_cmd') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__run_cmd') with nowait
        goto skip_sp__run_cmd
        end
    if @ver>091127
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__run_cmd') with nowait
        goto skip_sp__run_cmd
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__run_cmd') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__run_cmd'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__run_cmd]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091127\s.zaglio: added @print and use of sp__print_table
    v:090925\s.zaglio: some tips
    v:090911\s.zaglio: added help
    v:090909\s.zaglio: added special replacement for %@@servername% and %db_name()%
    v:081119\S.Zaglio: added tmp_table existance test and create
    v:081118\S.Zaglio: extended with parameters and external(online)file support
    v:081016\S.Zaglio: extended @cmd to 4000 chars
    v:080926\S.Zaglio: added @dbg param
    v:080806\S.Zaglio: added set nocount
    v:080704\S.Zaglio: run external command appendig result to temp table (name returned if null)
    t:sp__run_cmd ''dir "c:\"''
    t:sp__run_cmd ''dir %1'',''c:\'',@print=1
    c:sample to run a command that need an external text file
    t:sp__run_cmd ''ftp -s:%file% 2>ftp_log.txt & type ftp_log.txt'',@file=''cmd1<br>cmd2'',@dbg=1  --> generate file and replae %file% with name
    c:sample to write a batch to a file also to a remote net server
    t:sp__run_cmd @batch,@file=''\\svr\dir\batch.cmd|net_uid|net_pwd'',@dbg=1 --> generate batch file
    t:sp__run_cmd ''echo hello world!'',@file=''c:\windows\temp\test.txt'',@dbg=1 --> generate batch file
*/
CREATE proc [dbo].[sp__run_cmd]
    @cmd varchar(4000)=null,
    @v1 sql_variant=null,        -- replace %1 or will be added to @cmd
    @v2 sql_variant=null,        -- replace %2
    @v3 sql_variant=null,        -- replace %3
    @v4 sql_variant=null,        -- replace %4
    @v5 sql_variant=null,        -- ...
    @v6 sql_variant=null,
    @v7 sql_variant=null,
    @v8 sql_variant=null,
    @v9 sql_variant=null,
    @file varchar(4000)=null,    -- txt content or path|uid|pwd
    @nooutput bit=0,
    @tmp_table sysname=null output,
    @nodrop bit=0,
    @print bit=0,
    @dbg bit=0
as
begin
set nocount on
if @dbg=1 exec sp__printf ''-- sp__run_cmd ------------------------------''
declare
    @proc sysname,
    @msg nvarchar(4000),
    @end_declare bit

select
    @proc=''sp__run_cmd''

if @cmd is null goto help

if not @v1 is null set @cmd=replace(@cmd,''%1'',convert(varchar(4000),@v1))
if not @v2 is null set @cmd=replace(@cmd,''%2'',convert(varchar(4000),@v2))
if not @v3 is null set @cmd=replace(@cmd,''%3'',convert(varchar(4000),@v3))
if not @v4 is null set @cmd=replace(@cmd,''%4'',convert(varchar(4000),@v4))
if not @v5 is null set @cmd=replace(@cmd,''%5'',convert(varchar(4000),@v5))
if not @v6 is null set @cmd=replace(@cmd,''%6'',convert(varchar(4000),@v6))
if not @v7 is null set @cmd=replace(@cmd,''%7'',convert(varchar(4000),@v7))
if not @v8 is null set @cmd=replace(@cmd,''%8'',convert(varchar(4000),@v8))
if not @v9 is null set @cmd=replace(@cmd,''%9'',convert(varchar(4000),@v9))

declare @sql varchar(4000)
if @tmp_table is null begin
    set @tmp_table=''[dbo].[tmp_''+convert(varchar(64),newid())+'']''
    set @sql=''create table ''+@tmp_table+'' (lno int identity, line nvarchar(4000))''
    if @dbg=1 exec sp__printf @sql
    exec(@sql)
    end
else
    begin
    if left(@tmp_table,1)=''#'' select @nodrop=1,@nooutput=1
    if dbo.fn__exists(@tmp_table,''U'')=0
        begin
        set @sql=''create table ''+@tmp_table+'' (lno int identity, line nvarchar(4000))''
        if @dbg=1 exec sp__printf @sql
        exec(@sql)
        end
    end

declare @crlf varchar(2) set @crlf=char(13)+char(10)
declare @file_name varchar(1024)
declare @uid sysname
declare @pwd sysname
declare @i int

set @cmd=replace(@cmd,''%@@servername%'',@@servername)
set @cmd=replace(@cmd,''%db_name()%'',db_name())

if charindex(''%file%'',@cmd)>0 begin
    exec sp__get_temp_dir @file_name out
    set @file_name=''"''+@file_name+''\''+convert(varchar(64),newid())+''.txt"''
    set @cmd=replace(@cmd,''%file%'',@file_name)
    set @file=replace(@file,''<br>'',@crlf)
    if @dbg=1 exec sp__printf ''@file=%s @text=%s'',@file_name,@file
    exec sp__file_write @file_name,@text=@file,@dbg=@dbg
end
else begin
    if not @file is null begin
        set @file_name=dbo.fn__str_at(@file,''|'',1)
        set @uid      =coalesce(dbo.fn__str_at(@file,''|'',2),'''')
        set @pwd      =coalesce(dbo.fn__str_at(@file,''|'',3),'''')
        if @uid<>'''' or @pwd<>'''' begin
            set @i=charindex(''\\'',@file_name)
            if @i<>0 set @i=@i+2
            set @i=charindex(''\'',@file_name,@i)
            set @sql=''net use ''+substring(@file_name,1,@i-1)+'' ''+@pwd+'' /user:''+@uid
            set @sql=''insert into ''+@tmp_table+'' exec master..xp_cmdshell ''''''+dbo.fn__inject(@sql)+''''''''
            if @dbg=1 exec sp__printf @sql
            exec(@sql)
        end
        exec sp__file_write @file_name,@text=@cmd,@dbg=@dbg
        if @uid<>'''' or @pwd<>'''' begin
            set @sql=''net use ''+substring(@file_name,1,@i-1)+'' /delete''
            set @sql=''insert into ''+@tmp_table+'' exec master..xp_cmdshell ''''''+dbo.fn__inject(@sql)+''''''''
            if @dbg=1 exec sp__printf @sql
            exec(@sql)
        end
        goto ret
    end -- batch write form
end -- if %file%

set @sql=''insert into ''+@tmp_table+'' (line) values(''''''+dbo.fn__inject(@cmd)+'''''')''
if @dbg=1 exec sp__printf @sql
exec(@sql)
set @sql=''insert into ''+@tmp_table+'' exec master..xp_cmdshell ''''''+dbo.fn__inject(@cmd)+''''''''
if @dbg=1 exec sp__printf @sql
exec (@sql)
if not @file_name is null begin
    set @cmd=''del /q ''+@file_name
    set @sql=''insert into ''+@tmp_table+'' exec master..xp_cmdshell ''''''+dbo.fn__inject(@cmd)+''''''''
    if @dbg=1 exec sp__printf @sql
    exec (@sql)
    end
if @nooutput=0
    begin
    if @print=0
        begin
        select @sql=''select * from ''+@tmp_table+'' order by lno''
        exec(@sql)
        end
    else
        exec sp__print_table @tmp_table
    end
if @nodrop=0 begin set @sql=''drop table ''+@tmp_Table exec(@sql) end
goto ret

help:
    select @msg =''Use:\n''
                +''\tsp__run_cmd ''''dir %1'''',''''c:\''''\n''
    exec sp__usage @proc,@msg
    select @msg=null
ret:
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__run_cmd:

-- ================================================================= sp__run_dts
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__run_dts',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090121
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__run_dts') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__run_dts') with nowait
        goto skip_sp__run_dts
        end
    if @ver>090121
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__run_dts') with nowait
        goto skip_sp__run_dts
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__run_dts') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__run_dts'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__run_dts]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090121\S.Zaglio: changed name from sp__execute_DTS
*/
CREATE PROC sp__run_dts
    @PkgName nvarchar(255),             -- Package Name (Defaults to most recent version)
    @param1 sysname=null,
    @value1 sysname=null,
    @Server nvarchar(255)=null,
    @ServerPWD nvarchar(255) = Null,        -- Server Password if using SQL Security to load Package (UID is SUSER_NAME())
    @IntSecurity bit = 0,            -- 0 = SQL Server Security, 1 = Integrated Security
    @PkgPWD nvarchar(255) = ''''        -- Package Password
AS

/* examples:
    -- for running SQL server security authorization
    spExecuteDTS ''127.0.0.1'', ''dtsImportData'', ''sa''
    --/ or /--
    spExecuteDTS ''127.0.0.1'', ''dtsImportData'', ''sa'', 0

    -- for running SQL server security authorization
    spExecuteDTS ''127.0.0.1'', ''dtsImportData'', ''sa'', 1
*/

SET NOCOUNT ON
/*
    Return Values
    - 0 Successfull execution of Package
    - 1 OLE Error
    - 9 Failure of Package
*/

if @Server is null set @Server=@@servername

DECLARE @hr int, @ret int, @oPKG int, @Cmd nvarchar(1000)

-- Create a Pkg Object
EXEC @hr = sp_OACreate ''DTS.Package'', @oPKG OUTPUT
IF @hr <> 0
BEGIN
    PRINT ''***  Create Package object failed''
    EXEC sp__displayoaerrorinfo @oPKG, @hr
    RETURN 1
END

-- Evaluate Security and Build LoadFromSQLServer Statement
IF @IntSecurity = 0
    SET @Cmd = ''LoadFromSQLServer("'' + @Server +''", "'' + SUSER_SNAME() + ''", "'' + @ServerPWD + ''", 0, "'' + @PkgPWD + ''", , , "'' + @PkgName + ''")''
ELSE
    SET @Cmd = ''LoadFromSQLServer("'' + @Server +''", "", "", 256, "'' + @PkgPWD + ''", , , "'' + @PkgName + ''")''

EXEC @hr = sp_OAMethod @oPKG, @Cmd, NULL

IF @hr <> 0
BEGIN
        PRINT ''***  LoadFromSQLServer failed''
        EXEC sp__displayoaerrorinfo @oPKG , @hr
        RETURN 1
END

-- set parameters
if not @param1 is null begin
    EXEC @hr = sp_OAMethod @oPKG, ''AddGlobalVariable'', @Name = @param1, @Value = @value1
    IF @hr <> 0 PRINT ''Cant Add Global Var''
end




-- Execute Pkg
EXEC @hr = sp_OAMethod @oPKG, ''Execute''
IF @hr <> 0
BEGIN
        PRINT ''***  Execute failed''
        EXEC sp__displayoaerrorinfo @oPKG , @hr
        RETURN 1
END

-- Check Pkg Errors
EXEC @ret=sp__DisplayPkgErrors @oPKG

-- Unitialize the Pkg
EXEC @hr = sp_OAMethod @oPKG, ''UnInitialize''
IF @hr <> 0
BEGIN
        PRINT ''***  UnInitialize failed''
        EXEC sp__displayoaerrorinfo @oPKG , @hr
        RETURN 1
END

-- Clean Up
EXEC @hr = sp_OADestroy @oPKG
IF @hr <> 0
BEGIN
    EXEC sp__displayoaerrorinfo @oPKG , @hr
    RETURN 1
END

RETURN @ret'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__run_dts:

-- ================================================================= sp__run_sql
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__run_sql',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130605
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__run_sql') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__run_sql') with nowait
        goto skip_sp__run_sql
        end
    if @ver>130605
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__run_sql') with nowait
        goto skip_sp__run_sql
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__run_sql') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__run_sql'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__run_sql]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130605\s.zaglio: removed printf deprecated parameters
    v:091018\s.zaglio: NB: to remake&reduce using sp__loop
    v:090130\S.Zaglio: added a note about an error unmanaged
    v:090127\S.Zaglio: updated use of fn__servername
    v:090121\S.Zaglio: added @err_msg out
    v:081230\S.Zaglio: added @noexec to show only syntax
    v:081224\S.Zaglio: added quoted name management
    v:081223\S.Zaglio: added multilevel @dbg and corrected a bug on import of remote trace
    v:081218\S.Zaglio: replace @import_remote_trace with @trace_import
    v:081211\S.Zaglio: added @simul param to count rows will affected
    v:081209\S.Zaglio: corrected a bug on import of remote not ordered log
    v:081201\S.Zaglio: added @trace_proc for sp__trace
    v:081128\S.Zaglio: added relation of msg err to id with ref_id
    v:081126\S.Zaglio: complete rewrite of old one (now sp__run_sql_old)
    t:
        begin
        declare @r int,@rs bigint,@e int
        -- test simple local sql
        exec @r=sp__run_sql ''select top 3 * from sysobjects'',@rows=@rs out ,@err=@e out, @dbg=1
        exec sp__printf ''@r=%d @rows=%d @err=%d'',@r,@rs,@e
        -- test error
        exec @r=sp__run_sql ''select tap 3 * from sysobjects'',@rows=@rs out ,@err=@e out, @dbg=1
        exec sp__printf ''@r=%d @rows=%d @err=%d'',@r,@rs,@e
        -- test multiple server & db
        exec @r=sp__run_sql ''select top 3 *,rand(id) as ord from sysobjects order by ord'',
                @rows=@rs out ,@err=@e out, @svrs=''WSJ-VRT|SELFLINK|SELFLINK'',
                @dbs=''master|msdb|tempdb'', @trace=1,@dbg=1
        exec sp__printf ''@r=%d @rows=%d @err=%d'',@r,@rs,@e
        end
*/
CREATE proc [dbo].[sp__run_sql]
    @sql nvarchar(4000)=null,-- use /*o:???*/ for obj replacement
    @v1 sql_variant=null,    -- replacer for first %s or %d
    @v2 sql_variant=null,    -- replacer for next %s or %d
    @v3 sql_variant=null,    -- replacer for next %s or %d
    @v4 sql_variant=null,    -- replacer for next %s or %d
    @svrs nvarchar(4000)=null, -- must be a linked server, can be multiple svr1|svr2
    @dbs  nvarchar(4000)=null,
    @rows bigint=null out,     -- return inside/remote rowcount
    @err int=null out,
    @err_msg nvarchar(4000)=null out,
    @trace bit=0,
    @trace_import bit=1,
    @trace_proc sysname=null,
    @simul bit=0,
    @dbg smallint=0,
    @print bit=0,
    @noexec bit=0
as
begin
if @dbg<0 begin exec sp__printf ''@@nestlevel=%d'',@@nestlevel end
set nocount on
set @rows=null
if @sql is null goto help
-- initialization
set @sql=replace(@sql,''"'','''''''')
set @sql=dbo.fn__printf(@sql,@v1,@v2,@v3,@v4,null,null,null,null,null,null)
if @svrs is null set @svrs=dbo.fn__servername(null)
if @dbs is null set @dbs=db_name()
-- check extensions
declare @replacer sysname,@obj sysname
if @sql like ''%/*%[%][%]%*/%'' or @sql like ''%/*%[_]%[%][%]%*/%'' begin -- t: sp__run ''print "/*sp_%*/"''
    print ''replacer not implemented'' goto ret
end
declare @crlf nvarchar(2) set @crlf=char(13)+char(10)
-- tracer
declare @x1 int, @x2 int
declare @tsql nvarchar(512)
set @tsql='' if exists (select * from dbo.sysobjects where id = object_id(N''''[dbo].[log_trace]'''') ''+
          ''and OBJECTPROPERTY(id, N''''IsUserTable'''') = 1) ''
-- parameters
declare @ok int set @ok=dbo.fn__ok()
declare @r int
declare @params sysname  set @params=''@x1 int out,@x2 int out, @rows bigint out,@err int out, @r int out''
declare @returns sysname set @returns=''@x1=@x1 out,@x2=@x2 out,@rows=@rows out,@err=@err out,@r=@r out''
-- loops
declare @svr sysname, @db sysname, @osql nvarchar(4000)
declare @n_dbs int, @n_svrs int,@i int,@n int
set @n_dbs=dbo.fn__str_count(@dbs,''|'')
set @n_svrs=dbo.fn__str_count(@svrs,''|'')
set @i=1
set @osql=@sql
set @rows=0 set @err=null
while (@i<=@n_svrs) begin
    set @sql=@osql
    set @svr=dbo.fn__str_at(@svrs,''|'',@i)
    if @n_dbs>1 set @db=dbo.fn__str_at(@dbs,''|'',@i) else set @db=@dbs
    set @i=@i+1
    if dbo.fn__servername(@svr)=@svr set @svr=''''
    if @db=db_name() set @db=''''
    if @svr<>'''' set @svr=dbo.fn__sql_quotename(@svr)
    if @db<>'''' set @db=dbo.fn__sql_quotename(@db)
    if not @replacer is null set @sql=replace(@sql,@replacer,@obj)
    if @trace=0 and @svr='''' and @db<>'''' set @sql=''use ''+@db+ '' ''+@sql
    if @svr<>'''' or @db<>'''' begin
        if @simul=1 set @sql=''begin transaction ''+@sql        set @sql=@sql+'' select @err=@@error,@rows=@@rowcount ''
        if @simul=1 set @sql=@sql+''rollback transaction ''
    end
    -- read log_trace from other db of local server
    if @trace=1 and @trace_import=1 and @db<>'''' begin
        set @sql='' use ''+@db+'' ''+@tsql+'' select @x1=coalesce(max(id),0)+1 from dbo.log_trace ''+@sql+
                 '' ''+@tsql+'' select @x2=coalesce(max(id),0)   from dbo.log_trace ''
    end
    if @svr<>'''' and @db<>'''' set @sql=''exec ''+@svr+''.''+@db+''.dbo.sp_executesql N''+dbo.fn__sql_quote(@sql)+
                                     '',N''+dbo.fn__sql_quote(@params)+'',''+@returns
    set @x1=null set @x2=null set @r=null
    if @svr<>'''' or @db<>'''' begin
        declare @m_rows bigint set @m_rows=null
        declare @m_err int     set @m_err=null
        -- if @dbg>=@@nestlevel print ''begin declare @x1 int,@x2 int,@rows bigint,@err int,@r int ''+@sql+'' print @err print @rows end''
        if abs(@dbg)>=@@nestlevel and @n_svrs>1 select ''====== query server separator for ''+@svr+''.''+@db+'':''+
                                       @osql+'' ----------------------''
        if @print=1 or @noexec=1  print @sql
        if @noexec=0 exec sp_executesql @sql,@params,@x1=@x1 out,@x2=@x2 out,@rows=@m_rows out,@err=@m_err out,@r=@r out
        set @rows=@rows+@m_rows
        if coalesce(@err,0)=0 set @err=@m_err
    end
    else begin
        if @print=1 or @noexec=1 print @sql
        if @noexec=0 exec(@sql)
        select @err=@@error,@rows=@rows+@@rowcount
    end

    declare @trc_id int
    if @trace=1 exec sp__trace @sql,@last_id=@trc_id out, @proc=@trace_proc,@dbg=@dbg

    -- manage and trace error
    set @err_msg=''''
    if @err<>0 and @noexec=0 begin
        /* n.b.: in some situation this sp cause another error (maybe into stack) and leave
           without manage the error
        -- this will solve the above problem but for now don''t work
        create table #dbccout (id int identity,col1 nchar(77))
        insert into #dbccout exec (''dbcc outputbuffer(@@spid)'')
        select @i=min(id),@n=max(id) from #dbccout
        while (@i<=@n) begin
            select @err_msg = @err_msg + substring(col1, 62 + 1, 1) +
                                         substring(col1, 62 + 3, 1) +
                                         substring(col1, 62 + 5, 1) +
                                         substring(col1, 62 + 7, 1) +
                                         substring(col1, 62 + 9, 1) +
                                         substring(col1, 62 + 11, 1) +
                                         substring(col1, 62 + 13, 1) +
                                         substring(col1, 62 + 15, 1)
            from #dbccout where id=@i and left(col1, 8) <> replicate(''0'', 8)
            order  by col1
            set @i=@i+1
            end -- while
        drop table #dbccout
        -- exec sp__outputbuffer @err_msg out
        */
        if @svr is null begin -- if sql on server
            declare @lcid smallint
            select @lcid=lcid from master..syslanguages where langid=@@langid
            declare @msgerr sysname
            select @msgerr=description from master..sysmessages where error=@err and msglangid=@lcid
            if difference(@err_msg,@msgerr)<3 set @err_msg=@msgerr
        end
        if @svr is null
            set @err_msg=dbo.fn__printf(''#!(%d):%s'',@err,@err_msg,null,null,null,null,null,null,null,null)
        else
            set @err_msg=dbo.fn__printf(''#!(%d) on %s:%s'',@err,@svr,@err_msg,null,null,null,null,null,null,null)
        -- select top 1 @msg=description from master.dbo.sysmessages where error=@err
        if left(ltrim(@sql),7) in (''select '',''insert '',''delete '',''update '',''execute'',''exec @r'',''execute'')
            set @sql=dbo.fn__str_simplify(@sql,default)
        set @err_msg=@err_msg+'' (''+left(@sql,4000-len(@err_msg))+'')''
        if @trace=1 exec sp__trace @err_msg,@ref_id=@trc_id , @proc=@trace_proc else exec sp__printf @err_msg
    end -- if @err

    if (@svr<>'''' or @db<>'''') and @trace=1 and @trace_import=1 and not @x1 is null and not @x2 is null
    begin
        -- import remota trace log
        if @svr<>'''' and @db<>'''' set @sql=''insert into log_trace(spid,txt,ref_id) ''+
                                         ''select top 100 percent * from openquery(%svr%,"''+
                                            ''select spid,txt,ref_id from %db%.dbo.log_trace ''+
                                            ''where id between %x1% and %x2% order by id")''
        if @svr= '''' and @db<>'''' set @sql=''insert into log_trace(spid,txt,ref_id) ''+
                                         ''select top 100 percent spid,txt,ref_id from %db%.dbo.log_trace ''+
                                         ''where id between %x1% and %x2% order by id''
        exec sp__str_replace @sql out,''"|%svr%|%db%|%x1%|%x2%'','''''''',@svr,@db,@x1,@x2
        if @dbg>=@@nestlevel print @sql
        exec(@sql)
    end -- import remote trace
end -- while svrs
goto ret
help: exec sp__usage ''sp__run_sql''
ret:
return coalesce(@r,@err,@ok)
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__run_sql:

-- ================================================================= sp__run_vbs
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__run_vbs',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100919
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__run_vbs') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__run_vbs') with nowait
        goto skip_sp__run_vbs
        end
    if @ver>100919
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__run_vbs') with nowait
        goto skip_sp__run_vbs
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__run_vbs') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__run_vbs'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__run_vbs]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100919\s.zaglio: adapted to sp__err and other conventions
    v:100127\s.zaglio: added err return
    v:100104\s.zaglio: run a vbs
    c:originally from http://www.sql.ru/forum/actualthread.aspx?bid=1&tid=210409&hl
    t:
        declare @vbs nvarchar(4000),@out nvarchar(4000)
        select @vbs=''
            function main()
            hello="hello world"
            ''''print hello not allowed
            main=hello
            end function''
        exec sp__run_vbs @vbs,@out out
        print @out
        select @vbs=''
            option explicit
            sub main()
            dim hello
            hello="hello"
            end sub''
        exec sp__run_vbs @vbs,@out out
        print @out
*/
CREATE proc [dbo].[sp__run_vbs]
    @vbs nvarchar(4000)=null,
    @out nvarchar(4000)=null out,
    @start sysname=null,
    @dbg bit=0
as
begin
set nocount on;
declare @proc sysname,@i int,@j int,@ret int
declare @hr int, @obj int,@msg nvarchar(4000),
        @cmd sysname

select @proc=''sp__run_vbs'',@out=null,@ret=0

if @vbs is null goto help

if @start is null
    begin
    select @i=charindex(''function '',@vbs)
    select @j=charindex(''('',@vbs,@i)
    if @i>0 and @j>0
        select @start=ltrim(rtrim(substring(@vbs,@i+9,@j-@i-9)))
    else
        begin
        select @i=charindex(''sub '',@vbs)
        select @j=charindex(''('',@vbs,@i)
        if @i>0 and @j>0
            select @start=ltrim(rtrim(substring(@vbs,@i+4,@j-@i-4)))
        end
    end
if @start is null goto err_start
if @dbg=1 exec sp__printf ''func:%s\ncode:\n%s'',@start,@vbs

select @cmd=''scriptcontrol''
execute @hr = sp_oacreate @cmd, @obj out;
if @hr!=0 goto err

select @cmd=''language''
execute @hr = sp_oasetproperty @obj, @cmd, ''vbscript'';
if @hr!=0 goto err

select @cmd=''addcode''
execute @hr = sp_oamethod @obj, @cmd, null, @vbs;
if @hr!=0 goto err

select @cmd=''run''
execute @hr = sp_oamethod @obj, @cmd, @out out, @start
if @hr!=0 goto err

if @dbg=1 exec sp__printf ''out:%s'',@out
goto ret

err:
select @ret=@hr
declare @source varchar(255), @description varchar(255)
exec @hr = sp_oageterrorinfo @obj, @source out, @description out
exec @ret=sp__err ''cmd:%s; src:%s; des:%s; @out:%s'',@proc,
                  @p1=@cmd,@p2=@source,@p3=@description,@p4=@out
goto ret

err_start:  exec @ret=sp__err ''starting function/sub not found or specified'',@proc
            goto ret

help:
select @ret=-1

exec sp__usage @proc,''Parameters:
    @start  name of starting function or automatic
    ''

ret:
if @obj!=0 exec sp_oadestroy @obj
return @ret
end -- [sp__run_vbs]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__run_vbs:

-- ================================================================== sp__script
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=171214
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script') with nowait
        goto skip_sp__script
        end
    if @ver>171214
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script') with nowait
        goto skip_sp__script
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    d:111027.1000\s.zaglio:sp__scropt
    v:171214\s.zaglio:added %license%(@license_tag) in #vars
    v:131107\s.zaglio:corrected a no output when scripting by 0xID
    v:131030\s.zaglio:reenabled script of comments in case of table or view
    v:130729.1001\s.zaglio:managed #objs
    v:130729,130723,130719,130718,130712,130710\s.zaglio:arranging templates
    r:130708,130701\s.zaglio:some deprecation, some innovation;moving code from sp__script_group
    v:130506,130403\s.zaglio:added test of same ver, diff user;changed ovrchk behaviour
    v:130325,121218\s.zaglio:removed extra warning;again around problem of \\

    v:121212.1800\s.zaglio:semi-solved problem of "\"
    v:121108.1621\s.zaglio:reintroduced bug 111027 to understand where happen (see comm)
    v:121012\s.zaglio:now pass @opt to sp__script_code
    v:120731.1800\s.zaglio:ix->pk and remote sp__script
    v:120730.1700\s.zaglio:adding help for #src_def
    v:120717.1656\s.zaglio:adding opt OVRCHK
    v:120622\s.zaglio:added usage of related option
    v:120213,111028\s.zaglio:about obj as hex id;moved trigger scripting here
    v:111027\s.zaglio:solved problem with lines that end with \ that escape the return
    v:110629,110628\s.zaglio:a small bug on drop;added scripting of trigger on db
    v:110627,110614\s.zaglio:adapted to new hex codes;added encapsulation on drop
    v:110531,110510\s.zaglio:a small bug near help;removed external help
    v:110329\s.zaglio:removed @out (use ..tofile) and html opt and added number
    v:100919.1000\s.zaglio: a bug near scripting of obj of other db
    v:100919,100912\s.zaglio: added drop of constraint;resolved tremendous bug of print ''''
    v:100724,100718\s.zaglio: more dbg info;added go separator on multi obj
    v:100515,100509\s.zaglio: a minimal chk on obj existance;bug opt not passed to sp__script_table
    v:100418.2200\s.zaglio:added reverse option fro remote svr call
    v:100411,100405\s.zaglio:added synonym;adapted for sp__Script_group added go before raiserror
    v:100404,100403\s.zaglio:divided into more sps;added replacements and out to dir/file
    r:100328\s.zaglio:third remake of scripting utility
    t:sp__script ''sp__script'',@as=''sp__scropnt'',@dbg=1
    t:sp__script ''sp__script'',@opt=''select'',@dbg=1
    t:sp__script ''log_ddl'',@opt=''drop''
    t:sp__script ''sp__script'',@opt=''upgrade'',@dbg=1
*/
CREATE proc [dbo].[sp__script]
    @obj sysname=null,         -- single obj
    @as sysname=null,
    @license sysname=null,
    @opt sysname=null,         -- see help
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select
    @proc=object_name(@@procid),
    @ret=0,
    @opt=dbo.fn__str_quote(coalesce(@opt,''''),''|'')

if @obj is null goto help

-- ================================================================== declare ==

declare
    @type nvarchar(2),
    @drop nvarchar(4000),
    @i int, @n int,@sql nvarchar(4000),
    @db sysname,@sch sysname,@sch_id int,
    @lno_begin int,@lno_end int,
    @psep nchar(1),
    @pos int,@src_id int,@src_pos int,
    @crlf nvarchar(2),
    @tofile int,@vars_id int,
    @def bit,@upgrade bit,
    @nohdr bit,@nodecl bit,@nofot bit,
    @noprop bit,@notrg bit,
    @ver sysname,@aut sysname,
    @tgs sysname,@var_id int,
    @license_tag nvarchar(32)

-- ===================================================================== init ==

select
    @tgs=''rv'',
    @license_tag=''%license%'',
    @def=isnull(object_id(''tempdb..#src_def''),0),
    @psep=psep,
    @crlf=crlf,
    @upgrade=charindex(''|upgrade|'',@opt),
    @nohdr=charindex(''|nohdr|'',@opt),
    @nofot=charindex(''|nofot|'',@opt),
    @nodecl=charindex(''|nodecl|'',@opt),
    @tofile=charindex(''|tofile|'',@opt),
    @noprop=charindex(''|noprop|'',@opt),
    @notrg=charindex(''|notrg|'',@opt),
    @src_id=object_id(''tempdb..#src''),
    @var_id=object_id(''tempdb..#vars'')
from dbo.fn__sym()

if @src_id is null
    create table #src (lno int identity primary key,line nvarchar(4000))

if @upgrade=1 create table #out (lno int primary key,line nvarchar(4000))

-- for speed optimization, #tpl can be passed prefilled from sp__script_group
if object_id(''tempdb..#tpl'') is null
    begin
    create table #tpl (lno int identity,line nvarchar(4000))
--create table #tpl_sec(lno int identity,section sysname,line nvarchar(4000))
    create table #tpl_cpl(tpl binary(20),section sysname,y1 int,y2 int)
    end

-- init templates
if @upgrade=1
    begin
    exec @ret=sp__script_templates ''script''
    if @ret!=0 goto ret
    end
-- ===================================================================== body ==
if left(@obj,2)=''0x'' -- script directly from log_ddl
    begin
    exec sp__script_code @obj,@opt=@opt,@dbg=@dbg
    goto output
    end

-- special case for temp objs
if left(@obj,1)=''#''
    begin
    exec @ret=sp__script_code @obj
    goto ret
    end

select
    @db =db,
    @sch=sch,
    @obj=obj
from dbo.fn__parsename(@obj,0,1)

if @db!=db_name()
    begin
    -- roaming script to remote sp__script
    select @sql=''use ''+quotename(@db)+'' ''
               +''exec @ret=sp__script ''''''+@obj+'''''',''
               +''@opt=''''''+@opt+'''''',''
               +''@dbg=''+convert(sysname,@dbg)
    exec sp_executesql @sql,N''@ret int out'',@ret=@ret out
    if @@error!=0 goto err_rmt
    goto ret
    end

-- get local object info
select @sch_id=schema_id(@sch)

if @nohdr=0 -- 171214\s.zaglio -- and @upgrade=1
    begin
    /*
        sp__Script ''sp__script'',@opt=''upgrade'',@license=''test''
        only %license% can be replaced by @licence, because
        scripted code can contain tokens of itself
    */
    if not @var_id is null
        select
            @obj=case when id=''%obj%'' then cast(value as sysname)
                 else @obj end,
            @ver=case when id=''%ver%'' then cast(value as sysname)
                 else @ver end,
            @aut=case when id=''%aut%'' then cast(value as sysname)
                 else @aut end,
            @drop=case when id=''%drop%'' then cast(value as nvarchar(512))
                  else @drop end,
            @license=case when id=@license_tag then cast(value as sysname)
                  else @license end
        from #vars where id in (''%obj%'',''%ver%'',''%aut%'',''%drop%'',''%license%'')
    else
        begin
        select
            @type=typ,
            @drop=if_exists+@crlf+''    ''+drop_script
        from fn__sysobjects(@obj,@sch_id,''drop_script|if_exists'')

        select
            @ver=cast(cast(val1 as decimal(10,4)) as sysname),
            @aut=val2
        from fn__script_info(@obj,@tgs,0)
        end
    end

if @type is null
    select @type=typ
    from fn__sysobjects(@obj,@sch_id,default)

if @def=1
    begin
    select @src_pos=isnull(max(lno),ident_seed(''#src''))--+ident_incr(''#src'')
    from #src
    insert #src_def(xtype,cod,idx,flags)
    select @type,@obj,@src_pos,0
    end

-- ============================================================ script object ==

if @type is null goto err_typ

if @dbg=1 exec sp__printf ''-- db:%s, obj:%s, xt:%s'',@db,@obj,@type

if @upgrade=1
    begin
    if @nodecl=0 exec @ret=sp__script_template ''%declarations%''
    if @nohdr=0
        exec @ret=sp__script_template
             @section=''%obj_ver_chk%'',
             @tokens=''%obj%|%drop%|%ver%|%aut%'',
             @v1=@obj,@v2=@drop,@v3=@ver,@v4=@aut
    if @ret!=0 goto ret -- if not catched by caller
    end -- upgrade

-- mark last row before the insert of new code
select @lno_begin=isnull(max(lno),0) from #src

-- real script generation is demanded to sub proc
if @type in (''U'',''S'')
    exec sp__script_table @obj=@obj,@opt=@opt,@dbg=@dbg
else
    begin
    -- sp__script_code ''sp__Script''
    if @type in (''P'',''TR'',''FN'',''TF'',''IF'',''V'',''SN'',''TD'')
        exec sp__script_code @obj=@obj,@opt=@opt,@dbg=@dbg
    else
        goto err_typ
    end

-- sp__comment ''sp__script'',''script an object''

-- fist script line of code
select @lno_begin=(
    select top 1 lno
    from #src
    where lno>@lno_begin
    order by lno
    )

-- last script line
select @lno_end=max(lno) from #src

if @dbg=1 exec sp__printf ''-- begin:%d, end:%d'',@lno_begin,@lno_end

-- script properties
if (@upgrade=1 and @noprop=0 and left(@obj,1)!=''#'')
or @type in (''U'',''V'')
    exec sp__script_prop @obj=@obj,@dbg=@dbg

-- rename
if not @as is null
    update #src set line=replace(line,
        dbo.fn__sql_unquotename(ltrim(rtrim(@obj))),
        dbo.fn__sql_unquotename(ltrim(rtrim(@as)))
        )
    where lno between @lno_begin and @lno_end

-- apply vars
if not @license is null
    begin
    update #src set line=replace(line,@license_tag,@license)
    where charindex(''l:''+@license_tag,line)>0
    end

if @upgrade=1
    begin
    /*  121108\s.zaglio: the \ at end of line, means:"continue on next line"
        and give compile problem so I add a \\crlfcrlf as in this example
        print ''[1] foo\\

        bar''
        print ''[2] foo\\\

        bar''
        print ''[3] foo\\\


        bar''

        [1] foo        bar
        [2] foo\        bar
        [3] foo\\

                bar
    */
    update #src set
        line=case when right(line,1)=''\''
             then line+''\''+@crlf+
                  -- 121218\s.zaglio
                  case @tofile
                    when 0 then @crlf   -- out to console
                    else ''''             -- out to file
                  end
             else line
             end
    where lno between @lno_begin and @lno_end

    update #src set line=replace(line,'''''''','''''''''''')
    where lno between @lno_begin and @lno_end

    update #src set
        line=''exec dbo.sp_executesql @statement = N''''''
            +line
    where lno=@lno_begin
    update #src set line=line+''''''''
    where lno=@lno_end
    end -- quote

-- script triggers for table
if @type in (''U'')
and @notrg=0
    begin
    declare @trg sysname
    declare @tr_opt sysname
    select @tr_opt=@opt+''related''
    declare cs cursor local for
        select [name]
        from sysobjects o
        where parent_obj=object_id(@obj)
        and xtype=''TR''
        -- ?? ctype=''TR'' ?? maybe in the future
    open cs
    while 1=1
        begin
        fetch next from cs into @trg
        if @@fetch_status!=0 break
        insert #src(line) select ''''

        if @def=1
            begin
            select @src_pos=isnull(max(lno),ident_seed(''#src''))--+ident_incr(''#src'')
            from #src
            insert #src_def(xtype,cod,idx,flags)
            select ''TR'',@trg,@src_pos,0
            end

        if @db=db_name()
            exec sp__script_code @trg,@opt=@tr_opt,@dbg=@dbg
        else
            begin
            select @sql =''use [''+@db+''] exec sp__script_code @obj=''''''
                        +@trg+'''''',@opt=''''''+@tr_opt+'''''',@dbg=''+convert(sysname,@dbg)
            exec(@sql)
            if @@error!=0 goto err_rmt
            end

        end -- while of cursor
    close cs
    deallocate cs
    end -- triggers

if @upgrade=1 and @nofot=0
    begin
    exec @ret=sp__script_template @section=''%skip_obj%'',
                                  @tokens=''%obj%'',
                                  @v1=@obj
    if @ret!=0 goto ret
    end -- upgrade

-- ============================================================ output result ==
output:

if charindex(''|print|'',@opt)!=0
or @src_id is null
    exec sp__print_table ''#src''

if charindex(''|select|'',@opt)!=0
    select line from #src order by lno

goto ret

-- =================================================================== errors ==
err_onf:    exec @ret=sp__err ''object "%s" not found'',@proc,@p1=@obj
            goto ret
err_typ:    exec @ret=sp__err ''unknow type or object for "%s"'',@proc,@p1=@obj
            goto ret
err_rmt:    exec @ret=sp__err ''missing or wrong script utility on %s'',@proc,@p1=@db
            goto ret
-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    script any object (or try to do that)

Parameters
    @obj    name of object to script
            id of release (see sp__script_history)
    @as     rename object (very simple replace;do not work always)
    @opt    options (and see options below sub sp)
            upgrade wrap code for upgrade (sp__script_group)
            nodecl  leave out the upgrade''''s local variable declaration
            nohdr   leave out the upgrade''''s header
            nofot   leave out the upgrade''''s footer
            tofile  necessary because different behaviour of ending \\

            noprop  do not script extended property
            print   force print as text
            select  show result as select

Notes
    * can be called from sp__script_group
    * call sp__script_code if @obj is an proc,func,view,synonim,trigger
    * call sp__script_table if @obj is a table
    * if table #src_def is defined (called by sp__script_align),
      fill it with definition info of sub objects
        create table #src_def(
            xtype varchar(2),   -- sysobjects.xtype
            id int identity,    -- parent id
            rid int,            -- property parent id
            flags smallint,     -- flags depend on xtype (see below)
            cod sysname,        -- object or property name
            val sql_variant,    -- value of property
            idx int             -- relative source position start
            )

        xtype&description (* = managed)             flags
        =========================================== ============================
        AF = Aggregate function (CLR)
        C = CHECK constraint
        D = Default or DEFAULT constraint
        F = FOREIGN KEY constraint
        L = Log
        FN =*Scalar function
        FS = Assembly (CLR) scalar-function
        FT = Assembly (CLR) table-valued function
        IF =*In-lined table-function
        IT = Internal table
        IX =*Index
        PK =*PrimayKey
        UQ =*vincolo UNIQUE (il tipo è K)
        P =*Stored procedure
        PC = Assembly (CLR) stored-procedure
        RF = Replication filter stored procedure
        S = System table
        SN =*Synonym
        SQ = Service queue
        TA = Assembly (CLR) DML trigger
        TF =*Table function
        TR =*SQL DML Trigger
        TD =*trigger database
        TT = Table type
        U =*User table
        V =*View
        X = Extended stored procedure
        --= comments
''
-- show other helps
exec sp__script_code
exec sp__printf ''''
exec sp__script_table

ret:
return @ret
end -- sp__script'
exec sp__comment '[sp__script]','script an object'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script:

-- ============================================================== sp__script_act
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_act',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130116
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_act') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_act') with nowait
        goto skip_sp__script_act
        end
    if @ver>130116
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_act') with nowait
        goto skip_sp__script_act
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_act') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_act'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_act]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:action,trigger,db,store,application,personalization
    v:130116\s.zaglio: +quotename(@db)
    v:121003\s.zaglio: follow synonym
    v:120907\s.zaglio: connect application scripts to db trigger
*/
CREATE proc sp__script_act
    @sp sysname = null,
    @obj sysname = null,
    @cmd nvarchar(4000) = null,
    @idx int = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    @hash_sp int,@hash_obj int,
    @db sysname,@sql nvarchar(4000),
    @end_declare bit

-- =========================================================== initialization ==
select
    @db=db_name(),
    @sp=upper(@sp),         -- for compatibility with sp__script_store
    @obj=upper(@obj),       -- for compatibility with sp__script_store
    @hash_sp=dbo.fn__crc32(@sp),
    @hash_obj=dbo.fn__crc32(parsename(@obj,1)),
    @end_declare=1

-- ======================================================== second params chk ==
if '''' in (isnull(@sp,''''),isnull(@obj,''''),isnull(@cmd,'''')) -- or@opt=''||''
    goto help

-- follow synonym
exec sp__script_synonym @obj out,@obj,@opt=''path''
if isnull(parsename(@obj,3),@db)!=@db
    begin
    select
        @sp=quotename(@db)+''.''+isnull(parsename(@sp,2),'''')+''.''+parsename(@sp,1),
        @sql=N''exec @ret=''+quotename(parsename(@obj,3))
            +''..sp__script_act @sp,@obj,@cmd''
    exec sp_executesql
        @sql, N''@sp sysname,@obj sysname,@cmd nvarchar(4000),@ret int out'',
        @sp=@sp,@obj=@obj,@cmd=@cmd,@ret=@ret out
    goto ret
    end

if object_id(@sp) is null or object_id(@obj) is null goto err_unk

-- ===================================================================== body ==

update script_act set
    txt=@cmd,
    idx=@idx
where rid=@hash_sp
and pid=@hash_obj
if @@rowcount=0
    begin
    insert script_act(rid,pid,idx,txt)
    select @hash_sp,@hash_obj,@idx,@cmd
    end

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_unk:    select @e_msg=''not found "%s" or "%s"'',@e_p1=@sp,@e_p2=@obj goto err

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    connect create/drop/alter of an object to local application code

Notes
    data is stored into table "script_act"

Parameters
    @sp     is the name of storec procedure that register the dynamic code
            and with @obj is used a primary key
    @obj    name of object to connect
    @cmd    sql code to execute
    @opt    options (not used)

Examples
    exec sp__script_act @proc,@tbl,@cmd

-- list of application triggers --
''
select
    (select name from sys.objects where dbo.fn__crc32(upper(name))=s.rid) sp,
    (select name from sys.objects where dbo.fn__crc32(upper(name))=s.pid) obj,
    s.txt as sql
into #tmp
from script_act s

exec sp__select_astext ''select * from #tmp'',@header=1

drop table #tmp

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_act'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_act:

-- ============================================================ sp__script_alias
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_alias',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_alias') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_alias') with nowait
        goto skip_sp__script_alias
        end
    if @ver>121018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_alias') with nowait
        goto skip_sp__script_alias
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_alias') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_alias'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_alias]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:script,utility
    v:121018\s.zaglio: a bug near @opt
    v:120528\s.zaglio: renamed #objs in #alias_objs to avoid conflict
    v:120523\s.zaglio: a non correct err msg when target is a synonym
    v:120521\s.zaglio: a bug near dynamic sql
    v:120516\s.zaglio: adapted to new fn__script_sysobjs
    v:120504.1446\s.zaglio: added uses of synonyms
    v:111205\s.zaglio: added show of source when error occur in compilation
    v:110415\s.zaglio: added more table exclusions
    v:110213\s.zaglio: added more help for debug
    v:101113\s.zaglio: added support of excludes with %
    v:100724\s.zaglio: a bug around bits
    v:100722\s.zaglio: test destination to overrides/create correctly;+@excludes
    v:100721\s.zaglio: excluded sp_info,help
    v:100720\s.zaglio: generate a caller of objects of other db
    t:sp_config ''web%''
*/
CREATE proc [dbo].[sp__script_alias]
    @db sysname     = null,
    @like sysname   = null,
    @excludes nvarchar(4000) = null,
    @opt sysname    = null,
    @dbg int        = 0
as
begin
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname,  @ret int -- standard API: 0=OK -1=HELP, any=error id
select @proc=object_name(@@procid), @ret=0,
       @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @like is null select @like=''%''

if @db is null goto help

if @db=db_name() goto err_nodb

-- drop table #alias_objs
create table #alias_objs(
    id int identity,obj sysname,xtype sysname,
    flds nvarchar(4000),types nvarchar(4000),
    nulls nvarchar(4000) null, outs nvarchar(4000) null
    )

create table #excludes (obj sysname)
insert #excludes select ''sp_info''
insert #excludes select ''sp_find''
insert #excludes select ''help''
insert #excludes select ''hashbytes''
insert #excludes select cod from fn__script_sysobjs((select obj from tids))
insert #excludes select token from dbo.fn__str_params(@excludes,'','',default)
-- expand wild tokens
insert #excludes
    select o.name
    from #excludes e
    join sysobjects o on o.name like e.obj
    where e.obj like ''%[%]%''
delete from #excludes where obj like ''%[%]%''

declare
    @sql nvarchar(4000),
    @h1 sysname,@h2 sysname,@h3 sysname,@h4 sysname

-- select * from fn__script_sysobjs((select obj from tids))

select
    @h1=''/*  leave this'',
    @h2=''    g:sp__script_alias'',
    @h3=''    v:''+convert(sysname,getdate(),12)+''\generated by ''+@proc,
    @h4=''*/'',

    @sql=''
    insert #alias_objs (obj,xtype,flds,types)
    select
        o.name, o.xtype,
        ''+quotename(@db)+''.dbo.fn__flds_of(o.name,'''','''',null) flds,
        ''+quotename(@db)+''.dbo.fn__flds_type_of(o.name,''''|'''',null) types
    from ''+quotename(@db)+''..sysobjects o
    -- excludes extension/support system object tables
    left join fn__script_sysobjs((select obj from tids)) so
    on right(o.name,len(so.cod)+1)=''''_''''+so.cod
    where xtype in (''''tf'''',''''if'''',''''fn'''',''''p'''')
    and name like ''''''+@like+''''''
    and not name like ''''%[_][_]%''''  -- not the utilities
    and not name like ''''dt[_]%''''    -- not the mssql dt tables
    and not name like ''''tbl[_]%''''   -- not the utility tables
    and not name like ''''bak[_]%''''   -- not the backup tables
    and not name like ''''%bak''''      -- not the backup tables
    and not name like ''''tmp%''''      -- not the temp table
    and not name like ''''[_]%''''      -- not the temp table
    and not name like ''''0%''''        -- not the temp table
    and not name in(select obj from #excludes)   -- special local sp related to sp__info
    order by xtype,name

    -- select * from #alias_objs
    -- select * from #excludes

    declare
        @i int,@n int,@name sysname,@xt nvarchar(4),
        @nulls nvarchar(4000),@outs nvarchar(4000)
    select @i=min(id),@n=max(id) from #alias_objs
    while (@i<=@n)
        begin
        select @name=''''''+quotename(@db)+''..''''+obj,@xt=xtype
        from #alias_objs where id=@i
        if @xt=''''P''''
            begin
            select @nulls=null,@outs=null
            select @nulls=isnull(@nulls+'''','''','''''''')+convert(nchar(1),isnullable)
            from ''+quotename(@db)+''..syscolumns where id=object_id(@name) order by number,colorder
            select @outs =isnull(@outs+'''','''','''''''')+convert(nchar(1),isoutparam)
            from ''+quotename(@db)+''..syscolumns where id=object_id(@name) order by number,colorder
            update #alias_objs set nulls=@nulls, outs=@outs where id=@i
            end
        select @i=@i+1
        end -- while
''
if @dbg=1 exec sp__printf ''%s'',@sql
exec sp_executesql @sql
if @dbg=1 exec sp__select_astext ''select * from #alias_objs order by id''
create table #src(lno int identity,line nvarchar(4000))

-- select * from #alias_objs order by xtype
declare
    @i int,@n int,@m int,@xt sysname,@name sysname,
    @flds nvarchar(4000),@types nvarchar(4000),
    @nulls nvarchar(4000),@outs nvarchar(4000),
    @mssql2k bit,
    @done bit

select @mssql2k=dbo.fn__ismssql2k()

if charindex(''|nosyn|'',@opt)>0
    select @mssql2k=1
else
    select @mssql2k=0

select @i=min(id),@n=max(id) from #alias_objs
while (@i<=@n)
    begin
    select @done=0
    exec sp_executesql N''
    select
        @xt=xtype,@name=obj,
        @flds=flds,
        @types=types,
        @nulls=nulls,
        @outs=outs
    from #alias_objs where id=@i
    '',N''
    @i int,@xt sysname out,@name sysname out,
    @flds nvarchar(4000) out,@types nvarchar(4000) out,
    @nulls nvarchar(4000) out,@outs nvarchar(4000) out'',
    @i=@i,@xt=@xt out,@name=@name out,@flds=@flds out,@types=@types out,
    @nulls=@nulls out,@outs=@outs out

    select @m=dbo.fn__str_count(@flds,'','')
    truncate table #src

    if @mssql2k=1 and @xt=''FN''
        begin
        insert #src select @h1
        insert #src select @h2
        insert #src select @h3
        insert #src select @h4
        insert #src select ''create function ''+quotename(@name)+
                           case when @flds is null then '''' else ''('' end

        insert #src(line)
        select ''    ''+p.token+'' ''+t.token+
               case when p.pos<@m then '','' else '''' end as line
        from dbo.fn__str_params(@flds,'','',default) p
        join dbo.fn__str_params(@types,''|'',default) t on p.pos=t.pos
        where p.pos>1

        insert #src select case when @flds is null then '''' else '')'' end
        insert #src select ''returns ''+dbo.fn__str_at(@types,''|'',1)
        insert #src select ''as''
        insert #src select ''begin''
        insert #src select ''return ''+quotename(@db)+''.dbo.''+quotename(@name)+
                           case when @flds is null then '''' else ''('' end

        insert #src(line)
        select ''    ''+p.token+
               case when p.pos<@m then '','' else '''' end as line
        from dbo.fn__str_params(@flds,'','',default) p
        where p.pos>1

        insert #src select case when @flds is null then '''' else '')'' end
        insert #src select ''end''

        select @done=1
        end -- fn

    if @mssql2k=1 and @xt in (''IF'',''TF'')
        begin
        insert #src select @h1
        insert #src select @h2
        insert #src select @h3
        insert #src select @h4
        insert #src select ''create function ''+quotename(@name)+
                           case when @flds is null then '''' else ''('' end

        insert #src(line)
        select ''    ''+p.token+'' ''+t.token+
               case when p.pos<@m then '','' else '''' end as line
        from dbo.fn__str_params(@flds,'','',default) p
        join dbo.fn__str_params(@types,''|'',default) t on p.pos=t.pos
        where p.pos>1 and p.token like ''@%''

        insert #src select case when @flds is null then '''' else '')'' end
        insert #src select ''returns table ''
        insert #src select ''as''
        insert #src select ''return''
        insert #src select ''select * ''
        insert #src select ''from ''+quotename(@db)+''.dbo.''+quotename(@name)+
                           case when @flds is null then '''' else ''('' end

        insert #src
        select ''    ''+p.token+
               case when p.pos<@m then '','' else '''' end as line
        from dbo.fn__str_params(@flds,'','',default) p
        where p.pos>1 and p.token like ''@%''

        insert #src select case when @flds is null then '''' else '')'' end

        select @done=1
        end -- tf

    if @mssql2k=1 and @xt=''P''
        begin

        insert #src select @h1
        insert #src select @h2
        insert #src select @h3
        insert #src select @h4
        insert #src select ''create proc ''+quotename(@name)

        insert #src(line)
        select ''    ''+p.token+'' ''+t.token+
               case when dbo.fn__str_at(@nulls,'','',p.pos)=''1'' then ''=null'' else '''' end+
               case when dbo.fn__str_at(@outs, '','',p.pos)=''1'' then '' out''  else '''' end+
               case when p.pos<@m then '','' else '''' end as line
        from dbo.fn__str_params(@flds,'','',default) p
        join dbo.fn__str_params(@types,''|'',default) t on p.pos=t.pos

        insert #src select ''as''
        insert #src select ''exec ''+quotename(@db)+''.dbo.''+quotename(@name)

        insert #src(line)
        select ''    ''+p.token+
               case when dbo.fn__str_at(@outs, '','',p.pos)=''1'' then '' out''  else '''' end+
               case when p.pos<@m then '','' else '''' end as line
        from dbo.fn__str_params(@flds,'','',default) p

        select @done=1
        end -- sp

    if @mssql2k=0
        begin
        truncate table #src
        insert #src(line)
        select ''create synonym ''+quotename(@name)+
               '' for ''+quotename(@db)+''.dbo.''+quotename(@name)
        select @done=1
        end

    if @done=1
        begin
        if @dbg=1 exec sp__print_table ''#src''
        else
            begin
            declare @lxt nvarchar(2)            -- local obj type
            select @lxt=xtype from sysobjects where id=object_id(@name)

            -- test to ovverrides correctly
            if object_id(@name) is null
            or exists(select null from dbo.fn__script_info(@name,''g'',default)
                     where obj_id=object_id(@name) and val1=''sp__script_alias'')
            or ''SN''=@lxt            -- synonyms are overwritten
                begin
                exec sp__drop @name
                if @dbg=2
                    exec @ret=sp__script_compile @dbg=1
                else
                    exec @ret=sp__script_compile
                if @ret!=0 exec sp__print_table ''#src''
                end
            else
                exec @ret=sp__err ''local object "%s" is different'',@proc,@p1=@name
            end -- overrite/create
        end -- if done
    else -- not @done
        goto err_notp

    select @i=@i+1
    end -- while

drop table #src
drop table #alias_objs
goto ret

err_nodb:   exec @ret=sp__err ''cannot create self link'',@proc goto ret
err_notp:   exec @ret=sp__err ''not managed type "%s" for "%s"'',@proc,@p1=@xt,@p2=@name

help:
exec sp__usage @proc,''
Scope
    generate a local call to a sp/fn of a twin/origin/master database in mssql2k
    in mssql2k5 create a shynonym

Parameters
    @db         origin db
    @like       name of object to redirect (can use %; null means all sp and fn
    @excludes   name of obj to exclude, separated by comma (can use %)
    @opt        options     description
                nosyn       do not use synonym but classic mssql2k forward view/fn/sp

Notes
    If the sp/fn has a parameter @db_name, the sp/fn will be called padding to
    the original, del value of local "db_name()"

Example
    sp__script_alias ''''master_db'''',''''my_sps%''''

''
select @ret=-1
ret:
return @ret
end -- sp__script_alias'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_alias:

-- ============================================================ sp__script_align
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_align',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130729
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_align') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_align') with nowait
        goto skip_sp__script_align
        end
    if @ver>130729
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_align') with nowait
        goto skip_sp__script_align
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_align') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_align'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_align]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:table
    v:130729\s.zaglio:added tag t
    v:130327\s.zaglio:added @noidx and a bug near dropped index
    v:121004\s.zaglio:a bug near drop of disappeared cols
    v:121002\s.zaglio:added follow of symbolic src table
    v:120903\s.zaglio:added drop of constraints
    v:120903\s.zaglio:managed synonyms loop and no-out bug
    v:120827\s.zaglio:a bug near dst_db
    v:120801\s.zaglio:done and tested
    d:120727\s.zaglio:sp__table_align
    d:120727\s.zaglio:sp__script_copytable
    r:120727\s.zaglio:copy a table structure or align existing one
    t:sp__script_align_test
*/
CREATE proc sp__script_align
    @src    sysname = null,
    @dst    sysname = null,
    @opt    sysname = null,
    @dbg    int = 0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare
    @proc sysname, @err int, @ret int -- @ret: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if (@src is null or @dst is null) and @opt=''||'' goto help

-- ============================================================== declaration ==
declare
    @svr sysname,
    @src_db sysname,@dst_db sysname,
    @src_sch sysname,@dst_sch sysname,
    @src_obj sysname,@dst_obj sysname,
    @xtype nvarchar(4), @cod sysname,
    @opt_src sysname,   -- common options for sp__script
    @crc int,
    @crlf nvarchar(2),
    @sql nvarchar(max),
    @from int,          -- from code
    @to int,            -- to code
    @src_id int,
    @dst_id int,
    @notrg bit,         -- do not replicate trigger
    @run bit,
    @noidx bit,         -- do not replicate indexes
    @end_declare bit

create table #src(lno int identity,line nvarchar(4000))
create table #src_src(lno int identity,line nvarchar(4000))
create table #src_def(  -- drop table #src_def
    xtype nvarchar(2),  -- type: see table below
    id int identity,
    rid int,            -- propert parent
    flags smallint,     -- flags depend on tid
    cod sysname,        -- object or property name
    val sql_variant,    -- value of property
    idx int,            -- relative source position start,
    [end] int           -- extra fld for end of code
    )
create index #src_def on #src_def(xtype,cod)

-- tables columns compare to know which add and drop
create table #cmp_col (src sysname null,dst sysname null,col_def nvarchar(512))
create table #const(col sysname,name sysname)

-- final table with info about differencies
create table #cmp(
    xtype nvarchar(2),
    cod sysname,
    src_crc int,
    dst_crc int,
    src_from int,
    src_to int
    )
create index #cmp on #cmp(xtype,cod)

-- =========================================================== initialization ==
select
    @notrg=charindex(''|notrg|'',@opt),
    @noidx=charindex(''|noidx|'',@opt),
    @run=charindex(''|run|'',@opt),
    @opt_src=''noprop|nocmt''+case @notrg when 1 then ''|notrg'' else '''' end,
    @crlf=crlf
from fn__sym()

-- try to replace local synonym
select @src=base_object_name from sys.synonyms where name=@src
select @dst=base_object_name from sys.synonyms where name=@dst

-- normalize src
select @svr=svr, @src_db=isnull(db,db_name()), @src_sch=sch, @src_obj=obj
from dbo.fn__parsename(@src,default,1)
if not @svr is null and @svr!=dbo.fn__servername(Null) goto err_svr

select @src=quotename(@src_db)+''.''+isnull(@src_sch,'''')+''.''+@src_obj
select @src_id=object_id(@src)
if @dbg=1 exec sp__printf ''-- src_db:%s, src:%s'',@src_db,@src

if @src_id is null goto err_onf

if not @src_id is null and object_id(@src,''U'') is null goto err_syn

-- 121002\s.zaglio: if @db!=db_name() goto err_db
-- 121002\s.zaglio: select @xtype=xtype from sysobjects where id=@obj_id
-- 121002\s.zaglio: if @xtype!=''u'' goto err_ntt

-- normalize dst
select @svr=svr,
       @dst_db=isnull(db,@src_db),
       @dst_sch=isnull(sch,@src_sch), @dst_obj=obj
from dbo.fn__parsename(@dst,default,default)
if not @svr is null goto err_svr

select @dst=quotename(@dst_db)+''.''+isnull(@dst_sch,'''')+''.''+@dst_obj
select @dst_id=object_id(@dst)
if not @dst_id is null and object_id(@dst,''U'') is null goto err_syn

-- some checks
if db_id(@dst_db) is null or db_id(@src_db) is null goto err_dnf
if @dst_db=@src_db and @dst_sch=@src_sch and @dst_obj=@src_obj goto err_obj

if @dbg=1 exec sp__printf ''-- dst_db:%s, dst:%s'',@dst_db,@dst

-- ======================================================== second params chk ==
-- ===================================================================== body ==

-- ============================================================ script source ==
exec @ret=sp__script @src,@opt=@opt_src
if @ret!=0 goto err_scr

-- alter to destination name
insert #src_src select replace(line,@src_obj,@dst_obj) from #src order by lno
update #src_def set cod=replace(cod,@src_obj,@dst_obj)
-- from - to
update src_def set [end]=isnull(b.idx-1,99999)
from #src_def src_def
left join #src_def b on src_def.id+1=b.id

-- prepare code for check
select top 100 percent
    d.xtype,d.cod,s.lno,s.line,checksum(line) crc
into #src_crc
from #src_src s
join #src_def d
on s.lno between d.idx and d.[end]
order by s.lno

-- calc crc of new
declare cs_src cursor local for
    select distinct xtype,cod,idx,[end] from #src_def
open cs_src
while 1=1
    begin
    fetch next from cs_src into @xtype,@cod,@from,@to
    if @@fetch_status!=0 break

    select @crc=null
    select @crc=isnull(tbl.crc^@crc,tbl.crc)
    from #src_crc tbl
    where xtype=@xtype and cod=@cod

    insert #cmp(xtype,cod,src_crc,src_from,src_to)
    select @xtype,@cod,@crc,@from,@to

    end -- cursor cs_src
close cs_src
deallocate cs_src

-- ================================================ script destination object ==

if not @dst_id is null
    begin
    truncate table #src_def
    truncate table #src
    exec @ret=sp__script @dst,@opt=@opt_src
    if @ret!=0 goto err_scr

    -- prepare code for check
    select top 100 percent
        d.xtype,d.cod,s.lno,s.line,checksum(line) crc
    into #dst_crc
    from #src s
    join (
        select -- a.id,b.id,
            a.xtype,a.flags,a.cod,
            a.idx as start,isnull(b.idx-1,99999) [end]
        from #src_def a
        left join #src_def b on a.id+1=b.id
    ) d
    on s.lno between d.start and d.[end]
    order by s.lno

    -- calc crc of new
    declare cs_dst cursor local for
        select distinct xtype,cod from #src_def
    open cs_dst
    while 1=1
        begin
        fetch next from cs_dst into @xtype,@cod
        if @@fetch_status!=0 break

        select @crc=null
        select @crc=isnull(tbl.crc^@crc,tbl.crc)
        from #dst_crc tbl
        where xtype=@xtype and cod=@cod

        update #cmp set dst_crc=@crc where xtype=@xtype and cod=@cod
        if @@rowcount=0
            insert #cmp(xtype,cod,dst_crc) select @xtype,@cod,@crc

        end -- cursor cs_dst
    close cs_dst
    deallocate cs_dst

    truncate table #src
    insert #src select ''use [''+@dst_db+''] ''

    -- check if table is changed
    if exists(
            select null
            from #cmp
            where xtype=''u'' and src_crc!=dst_crc
            )
        begin
        select @sql=
        ''
        -- compare fields
        insert #cmp_col
        select
            src.name src_col,dst.name dst_col,
            dbo.fn__sql_def_col(
                                c.tablename,null,
                                dbo.fn__str_quote(c.columnname,''''[]''''),
                                null,c.typename,
                                c.[length],c.[precision],c.scale,
                                c.allownulls,c.dridefaultname,
                                c.dridefaultcode,c.[identity],
                                c.iscomputed,c.computedtext,null/*chkname*/,
                                null/*chkcode*/,c.collation,null,null
            ) as line
        from (
            select name
            from [%src_db%]..syscolumns
            where id=%src_id%
            ) src
        full outer join (
            select name
            from [%dst_db%]..syscolumns dst
            where dst.id=%dst_id%
            ) dst
        on src.name=dst.name
        left join [%src_db%]..fn__script_col(%src_id%) c on c.columnname=src.name
        where 1=1
        and (src.name is null or dst.name is null)

        -- acquire dst constraints
        insert #const(col,name)
        select c.name col,object_name(d.constid,db_id(''''%dst_db%'''')) cnts
        from [%dst_db%]..sysconstraints d
        join [%dst_db%]..syscolumns c on c.id=%dst_id% and c.colid=d.colid
        where d.id=%dst_id%
        ''
        exec sp__str_replace
                @sql out,
                ''%dst%|%dst_db%|%src_obj%|%src_id%|%dst_id%|%src_db%'',
                @dst,@dst_db,@src_obj,@src_id,@dst_id,@src_db
        if @dbg>0 exec sp__printsql @sql
        -- exec sp__printsql @sql
        exec(@sql)

        -- drop idx before columns to avoid column used problem

        -- drop pk if changed
        insert #src select ''''
        insert #src select ''-- drop pk if changed''
        insert #src
        select ''alter table ''+@dst_obj+'' drop constraint ''+quotename(cod)
        from #cmp
        where src_crc!=dst_crc  -- nulls excluded
        and xtype in (''pk'')

        -- drop index that not exists anymore
        insert #src select ''''
        insert #src select ''-- drop dest idx that not exists anymore''
        insert #src
        select ''drop index ''+quotename(@dst_obj)+''.''+quotename(cod)
        from #cmp
        where src_crc is null
        and xtype in (''ix'')

        insert #src select ''''
        insert #src select ''-- drop old constraints''
        insert #src
        select ''alter table ''+@dst_obj+'' drop constraint ''+quotename(con.name)
        from #cmp_col col
        join #const con on con.col=col.dst
        where src is null

        insert #src select ''''
        insert #src select ''-- drop old columns''
        insert #src
        select ''alter table ''+@dst_obj+'' drop column ''+quotename(dst)
        from #cmp_col
        where src is null

        insert #src select ''''
        insert #src select ''-- add new columns''
        insert #src select ''alter table ''+@dst_obj+'' add ''+cc.col_def
        from #cmp_col cc
        where cc.dst is null

        end -- table changed

        if @noidx=0
            begin
            insert #src select ''''
            insert #src select ''-- add new indexes''
            insert #src
            select line
            from #src_src src
            join #cmp cmp on src.lno between cmp.src_from and cmp.src_to
            where dst_crc is null
            and xtype in (''ix'',''pk'')

            insert #src select ''''
            insert #src select ''-- drop all changed or dropped indexes''
            insert #src
            select ''drop index ''+quotename(@dst_obj)+''.''+quotename(cod)
            from #cmp
            where (src_crc!=dst_crc or src_crc is null)
            and xtype in (''ix'')

            insert #src select ''''
            insert #src select ''-- re-create all changed indexes''
            insert #src
            select line
            from #src_src src
            join #cmp cmp on src.lno between cmp.src_from and cmp.src_to
            where src_crc!=dst_crc  -- nulls excluded
            and xtype in (''ix'',''pk'')
            end

        if @notrg=0
            begin
            insert #src select ''''
            insert #src select ''-- drop disappeared triggers''
            insert #src
            select ''drop trigger ''+quotename(cod)
            from #cmp cmp
            where (src_crc is null)
            and xtype in (''tr'')

            insert #src select ''''
            insert #src select ''-- align triggers''
            insert #src
            select line
            from #src_src src
            join #cmp cmp on src.lno between cmp.src_from and cmp.src_to
            where (src_crc!=dst_crc or dst_crc is null)
            and xtype in (''tr'')
            end -- notrg

    end -- script dst

else    -- if not exists

    begin
    truncate table #src
    insert #src select ''use [''+@dst_db+''] ''
    insert #src select line from #src_src order by lno
    end

-- debug info
if @dbg>0
    begin
    select
        *,  case
            when isnull(src_crc,0)!=isnull(dst_crc,0)
            then ''*''
            else ''''
            end [*]
    from #cmp
    end

-- ====================================================== script print or run ==

if @dbg>0 or @run=0
    exec sp__print_table ''#src''
if @run>0
    begin
    select @sql=isnull(@sql,'''')+isnull(line,'''')+@crlf from #src order by lno
    if @sql is null goto err_sql
    exec(@sql)
    if @@error!=0 goto err_sql
    end

-- ================================================================== dispose ==

dispose:
drop table #cmp
drop table #cmp_col
drop table #src_crc
drop table #src_src
if not object_id(''tempdb..#dst_crc'') is null drop table #dst_crc
drop table #src_def
drop table #src

goto ret

-- =================================================================== errors ==
err_svr:
exec @ret=sp__err ''server as source or destination is not managed'',
                  @proc
goto ret

err_db:
exec @ret=sp__err ''source object must come from current db'',
                  @proc
goto ret

err_obj:
exec @ret=sp__err ''cannot replicate object on itself'',
                  @proc
goto ret

err_syn:
exec @ret=sp__err ''destination object exists but is not a table'',
                  @proc
goto ret

err_onf:
exec @ret=sp__err ''source "%s" not found'',
                  @proc,@p1=@src_obj
goto ret

err_dnf:
exec @ret=sp__err ''destination db "%s" not found'',
                  @proc,@p1=@dst_db
goto ret

err_ntt:
exec @ret=sp__err ''source is not a table'',
                  @proc
goto ret

err_sql:
exec sp__printsql @sql
exec @ret=sp__err ''inside sql'',
                  @proc

err_scr:
goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    create a copy of table structure with different name and db;

Notes
    * create destination table if not exists
    * add index/pk if not exists on destination
    * drop destination index/pk if not exist in source
    * add/drop column if not exists or disappeared
    * comment/property are not aligned

Parameters
    @src    source table
    @dst    destination name (can contain a different db)
            can be a synonym that will be expanded
    @opt    options
            notrg   do not align triggers
            noidx   do not align indexes
            run     run script immediatelly
    @dbg    debug
            1 print code instead run it (same as not specify "run")

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_align'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_align:

-- ========================================================= sp__script_assembly
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_assembly',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121104
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_assembly') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_assembly') with nowait
        goto skip_sp__script_assembly
        end
    if @ver>121104
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_assembly') with nowait
        goto skip_sp__script_assembly
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_assembly') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_assembly'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_assembly]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:121104\s.zaglio: compile behaviour change
    r:121028\s.zaglio: done the scripting
    d:121027\s.zaglio: sp__assembly
    r:121020\s.zaglio: refined
    r:100424\s.zaglio: utility for assembly integration
*/
CREATE proc sp__script_assembly
    @assembly sysname = null,
    @cs nvarchar(max) = null,   -- c# source
    @opt sysname = null,
    @dbg int = null
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

declare
    @path nvarchar(1024),@cmd nvarchar(1024),
    @csc nvarchar(1024),@ext sysname,
    @dll nvarchar(1024),@src nvarchar(1024),
    @sql nvarchar(max),@tmp nvarchar(1024),
    @content varbinary(max),@i int,@crlf nvarchar(2),
    @asm_id int,@nets nvarchar(256)

declare @paths table(path nvarchar(1024),cod nvarchar(32))
create table #out(lno int identity,line nvarchar(4000))

select @crlf=crlf from fn__sym()

-- searcing compilers
select @cmd=''dir "%SystemRoot%"\"Microsoft.NET"\csc.exe/s/b''
insert @paths(path) exec xp_cmdshell @cmd

select @nets=''2.0|3.0|3.5|4.0|4.5''
-- 32
update p set cod=token+''x32''
from @paths p cross join dbo.fn__str_table(@nets,''|'')
where path like ''%\Framework\v''+token+''%\csc.exe''
-- 64
update p set cod=token+''x64''
from @paths p cross join dbo.fn__str_table(@nets,''|'')
where path like ''%\Framework64\v''+token+''%\csc.exe''

select top 1 @csc=path from @paths where charindex(cod,@opt)>0
if @csc is null
    begin
    select top 1 @csc=path from @paths where cod=''2.0x32''
    if @dbg=1 exec sp__printf ''-- default compiler:%s'',@csc
    end
else
    if @dbg=1 exec sp__printf ''-- forced compiler:%s'',@csc

if @csc is null or isnull(@assembly,'''')=''''
    goto help

if exists(select * from sys.assemblies where name=@assembly) goto err_asm

-- compile into new assembly
exec sp__get_temp_dir @path out
select @tmp=''sp__assembly_''+replace(cast(newid() as sysname),''-'',''_'')
select @src=@path+''\''+@tmp+''.cs''
select @dll=@path+''\''+@tmp+''.dll''
if @dbg=1 exec sp__printf ''-- src:%s, dll=%s'',@src,@dll
exec sp__file_write_stream @src,@txt=@cs

-- select @cmd=''cd "''+@path+''"&''+@csc ..
select @cmd=@csc+'' /t:library /out:''+@dll+'' ''+@src
if @dbg!=0 exec sp__printf ''-- cmd:%s'',@cmd
insert #out exec @ret=xp_cmdshell @cmd
if @ret!=0 or exists(select null from #out where line like ''%error%'')
    goto err_csc

select @sql=''create assembly %assembly% authorization [dbo] ''
           +''from ''''%dll%'''' with permission_set = safe''
exec sp__str_replace @sql out,''%assembly%|%dll%'',@assembly,@dll

exec(@sql)
if @@error!=0 goto err_cra

/*
select * from sys.assemblies
select * from sys.assembly_files
select * from sys.assembly_references
select      so.name, so.[type], schema_name(so.schema_id) as [schema],
            asmbly.name [assembly], asmbly.permission_set_desc, am.assembly_class,
            am.assembly_method
from        sys.assembly_modules am
inner join  sys.assemblies asmbly
        on  asmbly.assembly_id = am.assembly_id
        and asmbly.name not like ''microsoft%''
inner join  sys.objects so
        on  so.object_id = am.object_id
union
select      at.name, ''type'' as [type], schema_name(at.schema_id) as [schema],
            asmbly.name, asmbly.permission_set_desc, at.assembly_class,
            null as [assembly_method]
from        sys.assembly_types at
inner join  sys.assemblies asmbly
        on  asmbly.assembly_id = at.assembly_id
        and asmbly.name not like ''microsoft%''
order by    4, 2, 1
*/
select @asm_id=assembly_id from sys.assemblies where name=@assembly
if @asm_id is null goto err_asm
select @content=content from sys.assembly_files  where assembly_id=@asm_id
exec(''drop assembly ''+@assembly)

select @i=1,@sql=null
while @i<=len(@content)
    select @sql=isnull(@sql+''\''+@crlf,'''')
               +substring(dbo.fn__hex(substring(@content,@i,@i+64)),3,128),
           @i=@i+64
select @sql=''create assembly ''+@assembly+@crlf
           +''from 0x\''+@crlf+@sql+@crlf
           +''with permission_set = safe''+@crlf
exec sp__printsql @sql

/*
CREATE FUNCTION [fn_compress]           (
                 @compressedBlob varbinary(MAX))
            RETURNS varbinary(MAX)
            AS    EXTERNAL NAME %assembly%.%class%.%fn%;

select dbo.fn_compress(convert(varbinary(max),''test''))
*/


-- from http://www.codeproject.com/KB/database/blob_compress.aspx
/*
-- drop assembly utility_core
-- drop function fn__compress drop function fn__decompress
sp__script_assembly ''utility_core'',''
using System;
using System.IO;
using System.IO.Compression;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class utils
{
    // Setting function characteristics
    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic=true,
                                            DataAccess=DataAccessKind.None)]
    public static SqlBytes fn__compress(SqlBytes blob)
    {
        if (blob.IsNull)
            return blob;

        // Retrieving BLOB data
        byte[] blobData = blob.Buffer;

        // Preparing for compression
        MemoryStream compressedData = new MemoryStream();
        DeflateStream compressor = new DeflateStream(compressedData,
                                           CompressionMode.Compress, true);

        // Writting uncompressed data using a DeflateStream compressor
        compressor.Write(blobData, 0, blobData.Length);

        // Clossing compressor to allow ALL compressed bytes to be written
        compressor.Flush();
        compressor.Close();
        compressor = null;

        return new SqlBytes(compressedData);        // Returning compressed blob
    }

    public static SqlBytes fn__decompress(SqlBytes compressedBlob)
    {
        if (compressedBlob.IsNull)
            return compressedBlob;

        // Preparing to read data from compressed stream
        DeflateStream decompressor = new DeflateStream(compressedBlob.Stream,
                                           CompressionMode.Decompress, true);

        // Initializing variables
        int bytesRead = 1;
        int chunkSize = 10000;
        byte[] chunk = new byte[chunkSize];

        // Preparing destination stream to hold decompressed data
        MemoryStream decompressedData = new MemoryStream();

        try
        {
            // Reading from compressed stream
            while ((bytesRead = decompressor.Read(chunk, 0, chunkSize)) > 0)
            {
                // Writting decompressed data
                decompressedData.Write(chunk, 0, bytesRead);
            }
        }
        catch (Exception)
        {
            throw;                              // Nothing to do...
        }
        finally
        {
            decompressor.Close();               // Cleaning up
            decompressor = null;
        }
        return new SqlBytes(decompressedData);  // Returning a decompressed BLOB
    }
};
''
,@dbg=1
*/
dispose:
goto ret


-- =================================================================== errors ==
err_cra:
exec @ret=sp__err ''creating assembly'',@opt=''noerr''
goto ret

err_csc:
exec sp__print_table ''#out''
exec @ret=sp__err ''compiler'',@proc
goto ret

err_asm:
exec @ret=sp__err ''assembly already exists'',@proc
goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    compile a source stored into #src or from @file
    into a @file.dll

Notes
    * CLR must be active (see sp__util_advopt)
    * by default compile for .NET 2.0 32bit
    * actually only C# sources are considered

Parameters
    @assembly   assembly name
    @cs         is the c# source to compile and integrate
    @opt        options
                2.0x32  compile for .NET 2.0 32bit
                        others are: 3.0x32,3.5x32,4.0x32
                2.0x64  compile for .NET2.0 64bit
                        others are: 3.0x64,3.5x64,4.0x64
    @dbg        not used

''
exec sp__printf ''The C# compiler path is:%s\n'',@csc
select @csc=@csc+'' /?''
insert #out exec xp_cmdshell @csc
exec sp__print_table ''#out''

-- ===================================================================== exit ==

ret:
drop table #out
-- remove temporary files
select @cmd=''del /q ''+@dll
exec xp_cmdshell @cmd,no_output
select @cmd=''del /q ''+@src
exec xp_cmdshell @cmd,no_output

return @ret
end -- sp__assembly'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_assembly:

-- ============================================================= sp__script_code
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_code',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130729.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_code') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_code') with nowait
        goto skip_sp__script_code
        end
    if @ver>130729.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_code') with nowait
        goto skip_sp__script_code
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_code') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_code'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_code]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:130729.1000\s.zaglio:managed #objs
    v:121012\s.zaglio:option to remove top comments
    v:120730\s.zaglio:adding support for #src_def
    v:120717\s.zaglio:replaced {tab} with 4 spaces
    v:120622\s.zaglio:added related option
    v:120504\s.zaglio:managed drop option of history
    v:120213\s.zaglio:around @obj as hex value
    v:120207\s.zaglio:adapted to new log_ddl
    v:111028\s.zaglio:script of trigger into sp_execute
    v:110721\s.zaglio:added debug to test anomalous chars as � that boh... and used fn__ntext_to_lines
    v:110628\s.zaglio:added scripting of trigger on db
    v:110406\s.zaglio:on mssql2k5 uses sys.sql_modules instead of syscomments
    v:110329\s.zaglio:added @obj as # to script old revisions and removed @out
    v:100919.1000\s.zaglio:more compatible mssql2k
    v:100919\s.zaglio:strip end comments to not duplicate it ad every sp__script
    v:100411\s.zaglio:added synonym
    v:100328\s.zaglio: version 3.0 of scripting utility for views,proc,function,trigrs
    t:sp__script_code ''sp__script_code'',@dbg=1
    t:sp__script ''sp__script_code''
    t:sp__script_ole ''sp__script_code''
    t:sp__script_code ''obj_not_exist''
    t:sp__script ''sp__script'',@dbg=1
    t:
        create table #src(lno int identity,line nvarchar(4000))
        exec sp__Script_code ''sp__Script_code''
        select * from #src order by lno
        drop table #src
    t:
        create proc test as print ''hello''
        exec sp__script_code ''test''
        drop proc test
    t:
        create synonym stest for sp__script_code
        exec sp__script_code ''stest''
        exec sp__script ''stest''
        drop synonym stest
    t:sp__script_code 230   -- sp__script_trace
    t:sp__Script_code ''tr__script_trace_db''
    t:sp__script_code ''sp__script_code'',@dbg=1,@opt=''notcm''
*/
CREATE proc [dbo].[sp__script_code]
    @obj sysname=null,
    @opt sysname=null,
    @ntext ntext=null,  -- dummies
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select  @proc=object_name(@@procid),@ret=0,
        @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
if @obj is null goto help

if @dbg=1 exec sp__printf ''-- sp__script_code(%s,%s)'',@obj,@opt

-- unicode test:"日本"

declare
    @lno_begin int,@lno_end int,
    @id int,@db sysname,@sch sysname,@sch_id int,
    @obj_ex sysname,@obj_in sysname,
    -- @def bit,                        -- must stay in sp__Script
    @end_declare bit

declare @src table(lno int identity primary key,line nvarchar(4000))

declare
    @buf nvarchar(4000),@line nvarchar(4000),
    @s int,@k int,@i int,@n int,@j int,@old nvarchar(4000),
    @c nchar(1),@cr nchar(1),@lf nchar(1),@crlf nvarchar(2),
    @xtype nvarchar(4),@spaces varchar(4),@tab char(1)

select @cr=cr,@lf=lf,@crlf=crlf,@tab=tab,@spaces=''    '' from fn__sym()

if left(@obj,1)=''#''
    begin
    select top 1 @ntext=definition
    from tempdb.sys.sql_modules with (nolock)
    where object_id=object_id(''tempdb..''+@obj)
    print @ntext
    goto fill_src
    end

select
    -- @def=isnull(object_id(''tempdb..#src_def''),0),
    @db =parsename(@obj,3),
    @sch=parsename(@obj,2),
    @obj=parsename(@obj,1)
if @db is null select @db=db_name()
select @sch=[name],@sch_id=id from dbo.fn__schema_of(@obj)
select @obj_ex  = quotename(@db)+''.''
                + coalesce(quotename(@sch),'''')+''.''
                + quotename(@obj)

-- copy from syscomments or from log_ddl into internal table
if left(@obj,2)=''0x''
    begin
    select @id=dbo.fn__hex2int(@obj)
    select @ntext=txt
    from tids,log_ddl tbl
    where tbl.tid=tids.code and tbl.id=@id
    -- sp__script 80000007 -- sp__script_history
    if @@rowcount=0 goto err_nof
    end
else
    begin
    -- get type
    select @id=id,@xtype=xtype from sysobjects where [name]=@obj

    -- if is a synonym
    if @xtype=''SN''
        begin

        insert #src(line)
        select ''create ''+lower(sy.type_desc)+'' ''
               +sc.name + ''.'' + sy.name -- as synonym_name
               +'' for ''+sy.base_object_name
        from sys.synonyms sy
        join sys.schemas  sc on sc.schema_id = sy.schema_id
        where sy.[object_id] = @id
        goto ret
        end -- synonym

    if @id is null select @id=object_id from sys.triggers where [name]=@obj
    if @id is null goto err_nof
    select top 1 @ntext=definition
    from sys.sql_modules with (nolock)
    where object_id=@id
    end -- fill @blob

-- ##########################
-- ##
-- ## fill @src
-- ##
-- ########################################################
fill_src:
insert @src(line)
select line from fn__ntext_to_lines(@ntext,0 /*remove crlf */)
update @src set line=rtrim(line)

-- remote top comments /* */
if charindex(''|sqlite|'',@opt)>0
or charindex(''|notcm|'',@opt)>0
    begin
    select @line=null
    select top 1 @line=line
    from @src
    where line like ''/*%'' or line like ''--%''
    order by lno
    if @line like ''--%''
        delete from @src
        where lno<(
            select top 1 lno from @src
            where not line like ''--%''
            )
    -- NB: nested comment are not managed
    if @line like ''/*%''
        delete from @src
        where lno<=(
            select top 1 lno from @src
            where line like ''%*/''
            )
    end -- delete top comments

-- ########################################################

select @lno_begin=min(lno),@lno_end=max(lno)
from @src

-- strip blank line above
select top 1 @lno_begin=lno
from @src
where line!=''''
and lno>=@lno_begin
order by lno

select top 1 @lno_end=lno
from @src
where line!=''''
and lno<=@lno_end
order by lno desc

-- strip end comments to not duplicate it ad every sp__script
if exists(
    select top 1 null from @src
    where left(line,16)=''exec sp__comment''
    and lno<=@lno_end
    order by lno desc
    )
    select top 1 @lno_end=lno from @src
    where left(line,16)!=''exec sp__comment''
    and lno<=@lno_end
    order by lno desc

-- if is a trigger, require GO so we encapsulate into execute
if (@xtype=''TR'' and charindex(''|related|'',@opt)!=0)
or (left(@obj,2)=''0x'' and charindex(''|drop|'',@opt)!=0)
-- and charindex(''|DROP|'',@opt)=0
    begin
    -- note: this manage one single code
    update @src set
        line=replace(
                case when right(line,1)=''\'' -- this solve escape problem
                then line+'' ''
                else line
                end,'''''''',''''''''''''
                )
    update @src set line=''exec sp_executesql N''''''+line where lno=@lno_begin
    update @src set line=line+'''''''' where lno=@lno_end
    end -- encapsulate

if object_id(''tempdb..#src'') is null
    select line
    from @src
    where lno between @lno_begin and @lno_end
    order by lno
else
    insert #src(line)
    select replace(line,@tab,@spaces)
    from @src
    where lno between @lno_begin and @lno_end
    order by lno

-- nb: charindex search into 4000 chars max
goto ret

-- =================================================================== errors ==
err_nof:    exec @ret=sp__err ''object or release %s not found'',@proc,@p1=@obj
            goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope of %proc%
    script objects tha has code into syscomments(mssql2k) or sys.sql_modules(mssql2k5)
    or log_ddl

Notes
    {tab} is replaced with 4 spaces

Parameters
    @obj    is the object name
    @ntext  is for internal use
    #src    if declared store result into this otherwise output as select
    @opt    options     description
            drop        enclose code into sp_executesql statement
            related     when a trigger is under a table, code is
                        encapsulated into a sp_executesql
            notcm       do not script top comments

''


select @ret=-1

ret:
return @ret
end -- sp__script_code'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_code:

-- ========================================================== sp__script_compile
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_compile',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130605
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_compile') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_compile') with nowait
        goto skip_sp__script_compile
        end
    if @ver>130605
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_compile') with nowait
        goto skip_sp__script_compile
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_compile') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_compile'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_compile]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:130605\s.zaglio: removed printf deprecated parameters
    v:120920\s.zaglio:ms2k5 version because some strange @crlf loses
    v:111205\s.zaglio:added noalter option
    v:110415\s.zaglio:added error return value
    v:110324\s.zaglio:added @opt and dep
    v:110213\s.zaglio:about automatic alter
    v:111130\s.zaglio:improved recodnition of "create xxxx"
    v:100919.1000\s.zaglio:improved help and more compatible with mssql2k
    v:100703\s.zaglio:do not alter create table
    v:100612\s.zaglio:more help
    v:100424\s.zaglio:added -- noalter as pragma option
    v:100405\s.zaglio:retested compile of @obj (saving original before!!!)
    v:100404\s.zaglio:added control for 100 blocks and null lines and chunking
    v:100328\s.zaglio:renamed old sp__script_recompile & integrated sp__Scrit_run
    v:100118\s.zaglio:added to group script
    v:091018\s.zaglio: recompile a big procedure passe with #src
    t:sp__script_compile ''sp__script_compile'',@dbg=1
    t:sp__script_compile ''sp__script_table'',@dbg=1
    t:exec sp__script_compile ''flags'',@opt=''dep''
    t:exec sp__script_trace_db ''install''
*/
CREATE proc [dbo].[sp__script_compile]
    @obj sysname=null,      -- an sp/function/view to recompile or #src
    @opt sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select  @proc=object_name(@@procid),@ret=0,
        @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

declare
    @i int,@n int,@s int,@line nvarchar(4000),@id int,
    @crlf nchar(2),@cr nchar(1),@m int,@block nvarchar(4000),
    @trace bit,@noalter bit

declare @src table(lno int identity(1,1) primary key, line nvarchar(4000))

select
    @crlf=crlf,@cr=cr,
    @trace=charindex(''|trace|'',@opt),
    @noalter=charindex(''|noalter|'',@opt)
from dbo.fn__sym()

if @obj is null
and not object_id(''tempdb..#src'') is null
    select @obj=''#src''

if @obj is null goto help

-- read first line to determine if is a single or multiple script
if @obj=''#src''
    begin
    declare cs cursor local for
        select coalesce(line,N'''')
        from #src
        order by lno
    open cs

    fetch next from cs into @line

    if @line like ''--%script%''
        begin                                   -- multi script
        while 1=1
            begin
            fetch next from cs into @line
            if @@fetch_status!=0 break

            if @dbg=1 exec sp__printf @line
            exec(@line)
            if @@error!=0
                begin
                exec sp__printf ''error in:\%s'',@line
                select @ret=-2
                goto ret
                end
            end -- while
        close cs
        deallocate cs
        goto ret
        end -- execute line by line

    -- group lines of single script into blocks
    select @block='''',@n=0,@i=0
    while 1=1
        begin
        select @i=@i+1
        if @dbg=1 exec sp__printf ''%d %s'',@i,@line
        if len(@block)+len(@line)+len(@crlf)>4000
            begin
            insert @src select @block
            select @block='''',@n=@n+1
            end
        select @block=@block+@line+@crlf

        fetch next from cs into @line
        if @@fetch_status!=0 break
        end -- while

    if len(@block)>0    -- last chunk
        begin
        insert @src select @block
        select @block='''',@n=@n+1
        end

    if @n>100 goto err_max

    close cs
    deallocate cs
    goto run
    end -- load lines of single script into blocks


-- used as store for sequence of objects
create table #dep(
    id int identity,
    uses bit null,
    obj sysname,
    buildin sysname null,
    usr sysname null,
    comment sysname null,
    [level] int null
    )

if charindex(''|dep|'',@opt)>0
    exec sp__script_dep @obj,@opt=''uses''
else
    insert #dep(obj) select @obj

if @dbg=1 select * from #dep order by id

declare objs cursor local for
    select obj
    from #dep
    order by id
open objs
while 1=1
    begin
    select @obj=null
    fetch next from objs into @obj
    if @@fetch_status!=0 break

    select @id=object_id(@obj)
    if @dbg=1 exec sp__printf ''\n## fetched obj "%s" id "%d"'',@obj,@id
    if @trace=1 exec sp__printf ''-- recompiling: %s'',@obj

    -- load obj source info memory table
    if @id is null goto err_onf

    delete from @src

    insert into @src(line)
    select [text]
    from syscomments
    where id=@id
    order by colid

    -- change create into alter
    select top 1 @i=lno,@line=line from @src order by lno

    declare @key1 sysname,@key2 sysname
    select  @key1=case when pos=1 then token else @key1 end,
            @key2=case when pos=2 then token else @key2 end
    from dbo.fn__str_table(@line,'''') where pos in (1,2)

    -- specific generated code so existance is checked by caller
    select top 1 @n=lno,@line=line,@i=charindex(@crlf+''create '',@line)+len(@crlf)
    from @src where charindex(@crlf+''create '',@line)>0 order by lno

    declare @ln nvarchar(4000)
    select @ln=substring(@line,@i,charindex(@crlf,@line,@i)-@i)

    if @dbg=1 exec sp__printf ''-- recompile: chg create to alter at line:%d, pos:%d, ln:%s'',@n,@i,@ln
    /*if @dbg=1 begin
        -- sp__script_compile ''sp__script_compile'',@dbg=1
        exec sp__printf ''i=%d, n=%d'',@i,@n
        print substring(@line,@i,20)
    end*/
    select  @key1=case when pos=1 then token else @key1 end,
            @key2=case when pos=2 then token else @key2 end
    from dbo.fn__str_table(@ln,'''')
    where pos in (1,2)

    select @line=substring(@line,1,@i-1)
                +''alter''+substring(@line,@i+len(@key1),4000)

    if @dbg=1 exec sp__printf ''k1:%s, k2:%s, ln:%s'',@key1,@key2,@ln
    select @ln=substring(@line,@i-1,charindex(@crlf,@line,@i)-@i)

    update @src set line=@line
    where lno=@n

    if @dbg=1 exec sp__printf ''@i=%d'',@i

    run:
    /*
        select * into #tmp from @src order by lno
        exec sp__print_table ''#tmp''
        return 0
    */

    /* this generate the code to execute a source bigger than 4000 chrs
    declare @sql nvarchar(4000),@nullize nvarchar(4000)
    declare @i int,@n int
    exec sp__printf ''select top 1 @i=lno from @src order by lno''
    select @i=0,@n=99,@sql=''''
    while @i<=@n
        begin
        exec sp__printf ''declare @s%d nvarchar(4000);select @s%d=null,@s%d=line from @src where lno=@i+%d'',
                        @i,@i,@i,@i
        select @sql=@sql+''@s''+convert(sysname,@i)+
               case when @i<@n then ''+'' else '''' end+
               case when @i%12=0 then nchar(13)+nchar(10) else '''' end
        select @i=@i+1
        end
    exec sp__printf ''''
    exec sp__printf ''if @dbg=1 exec sp__printsql @s0''
    exec sp__printf ''''
    exec sp__printsql ''exec('',@sql,'')''
    -- copy and paste the result below, after close of comment
    */
    declare @sql nvarchar(max)
    select @sql= ''''
    select @sql=@sql+line from @src order by lno
    if @sql is null goto err_cod
    exec(@sql)
    select @ret=@@error
    if @ret!=0 exec sp__printsql @sql

    if @obj=''#src'' goto ret
    end -- while objs

dispose:
close objs
deallocate objs
goto ret

-- =================================================================== errors ==

err_max:    exec sp__err ''%d blocks but max 100 admitted'',@proc,@p1=@m
            goto ret
err_onf:    exec @ret=sp__err ''object not found'',@proc
            goto ret
err_cod:    exec @ret=sp__err ''inside code error'',@proc
            goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    Recompile an object or compile a source given in #src
    if 1st line is "-- script" esecute each line present in #src
    if 1st line is "-- noalter" do not automatically change create into alter
                                (see also option "noalter")

Parameters
    @obj    name of proc,view,function, etc.
    @opt    options
            dep         do not recompile @obj but all objects that use it
                        (sp__script_dep ''''tids'''',@opt=''''uses'''')
            noalter     do not automatically change

Table
    create table #src(lno int identity,line nvarchar(4000))
    insert #src(line) select ''''-- scripts''''
    insert #src(line) select ''''print ''''''''row1''''''''''''
    insert #src(line) select ''''print ''''''''row2''''''''''''
    exec sp__script_compile
    truncate table #src
    insert #src(line) select ''''create proc sp_test as print ''''''''sp_test''''''''''''
    exec sp__script_compile
    exec sp_test
    drop proc sp_test
    drop table #src
''
select @ret=-1

ret:
return @ret
end -- [sp__script_compile]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_compile:

-- ========================================================= sp__script_compress
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_compress',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130703
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_compress') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_compress') with nowait
        goto skip_sp__script_compress
        end
    if @ver>130703
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_compress') with nowait
        goto skip_sp__script_compress
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_compress') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_compress'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_compress]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:binary
    r:130703\s.zaglio: compress #src content and replace it with hex code
*/
create proc sp__script_compress
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    @i int,@n int,                      -- index, counter
    @sql nvarchar(max),
    @bin_sql varbinary(max),
    @line nvarchar(4000),
    @crlf nvarchar(2),
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    @crlf=crlf,
    @end_declare=1
from fn__sym()

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if object_id(''tempdb..#src'') is null goto help


-- =============================================================== #tbls init ==

-- ===================================================================== body ==

-- sp__script_group ''utility'',@opt=''bin''
select @n=count(*) from #src
exec sp__printf ''-- %d lines in #src'',@n
select @sql=
   stuff( (select @crlf+line
          -- select name
           from #src
           order by lno
           for xml path(''''), type).value(''.'', ''nvarchar(max)'')
        ,1,len(@crlf),'''')
exec sp__printsql @sql
select @bin_sql=dbo.fn__compress(cast(@sql as varbinary(max)))
select @n=len(@bin_sql)
exec sp__printf ''-- compressed size:%d'',@n

truncate table #src
select @i=1
while (@i<len(@bin_sql))
    begin
    select @line=substring(dbo.fn__hex(substring(@bin_sql,@i,64)),3,128)
    insert #src(line) select @line
    select @i=@i+64
    end

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    compress #src content and replace it with hex code

Parameters
    [param]     [desc]
    #src        the source code to compress
    @opt        options (not used)
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_compress'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_compress:

-- ============================================================= sp__script_data
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_data',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=111229
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_data') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_data') with nowait
        goto skip_sp__script_data
        end
    if @ver>111229
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_data') with nowait
        goto skip_sp__script_data
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_data') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_data'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_data]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:insert
    v:111229\s.zaglio: adapted to new sp__script_template
    r:111228\s.zaglio: adapting to new sp__script_template
    r:111227\s.zaglio: adapting to new sp__script_template
    r:111223\s.zaglio: adapting to new sp__script_template
    v:111103\s.zaglio: added bincols option and db check
    v:111031\s.zaglio: added N before ''
    v:110824\s.zaglio: adapted to new sp__script_template
    v:110707\s.zaglio: a small bug near ident
    v:110706.1221\s.zaglio: a bug near truncate and added restore
    v:110705\s.zaglio: added @opt
    v:110518\s.zaglio: injected strings with ''
    v:100919\s.zaglio: added insert from #tmp (useful to find error string or bin truncated)
    v:100601\s.zaglio: added @top and @where
    v:100514\s.zaglio: print script to insert data
    t:
        create table test_script_data(id int identity, a sysname, d datetime default(getdate()))
        insert test_script_data(a) select ''one''
        insert test_script_data(a) select ''two''
        exec sp__script_data ''test_script_data''
        exec sp__script_data ''test_script_data'',@where=''a="two"''
        exec sp__script_data ''test_script_data'',@top=1,@dbg=1
        select * into #t from test_script_data
        exec sp__script_data ''#t''   -- TODO
        drop table test_script_data
        drop table #t
*/
CREATE proc sp__script_data
    @tbl sysname=null,
    @where sysname=null,
    @top sysname=null,
    @opt sysname=null,
    @dbg bit=null
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0,@dbg=isnull(@dbg,0),
       @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @tbl is null goto help

if left(@tbl,1)!=''#'' and object_id(@tbl) is null goto err_noo
if left(@tbl,1)=''#'' and object_id(''tempdb..''+@tbl) is null goto err_noo

select @tbl=dbo.fn__sql_unquotename(@tbl)

if not @where is null and (dbo.fn__occurrence(@where,''"'') % 2)=0
    select @where=replace(@where,''"'','''''''')

declare
    @flds nvarchar(4000),
    @oflds nvarchar(4000),@merge bit,@restore bit,@scramble bit,
    @dst sysname,@n int, @src sysname,
    @excludes sysname,@noinc bit,
    @identity sysname                -- main section

declare @bincols table ([name] sysname)

if charindex(''|bincols:'',@opt)>0
    begin
    insert @bincols([name])
    select token
    from dbo.fn__str_table(dbo.fn__str_between(@opt,''|bincols:'',''|'',default),''|'')
    end -- bincols

create table #tpl(lno int identity primary key,line nvarchar(4000))
create table #tpl_sec(lno int identity,section sysname,line nvarchar(4000))

-- first define the template
insert #tpl(line)
select line
from dbo.fn__ntext_to_lines(''
%safety%:
if db_name()=''''%db%''''
    begin
    raiserror(''''for safety reasons I can not run on the same database'''',10,1) with nowait
    goto safe_skip
    end

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%before%:
%safety%

select top 0 * into [%dst%] from [%src%]    -- |merge|

alter table [%dst%] disable trigger all

set identity_insert [%dst%] on              -- |ident|

truncate table [%dst%]                      -- |where|
-- truncate table [%dst%]

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%after%:

exec sp__merge  "%src%",                    -- |merge|
                @from="%dst%",              -- |merge|
                @keys="???",                -- |merge|
                @flds_cmp="%flds%"          -- |merge|

set identity_insert [%dst%] off             -- |ident|

alter table [%dst%] enable trigger all

'',0)

select @identity=[name]
from syscolumns
where id=object_id(@tbl)
and columnproperty(id,[name],''IsIdentity'')=1

select
    @merge=charindex(''|merge|'',@opt),
    @restore=charindex(''|restore|'',@opt),
    @scramble=charindex(''|scramble|'',@opt),
    @noinc=charindex(''|noinc|'',@opt),
    @flds=dbo.fn__flds_of(@tbl,''|'',case when @noinc=1 then @identity else null end),
    @oflds=replace(dbo.fn__flds_quotename(@flds,''|''),''|'','',''),
    @dst=case when @merge=1 then ''#'' else '''' end + @tbl,
    @src=@tbl,
    @excludes=''''

create table #out (lno int identity(10,10),line nvarchar(4000))
create table #src (lno int identity,line nvarchar(4000))

if @identity is null and @noinc=0 select @excludes=@excludes+''|ident''
if @merge=0 select @excludes=@excludes+''|merge''

-- <fields>
insert #tpl_sec
select
    ''%fields%'',
    case
    when @scramble=1 and (t.[name] like ''%char'' or t.name in (''sysname''))
    then ''dbo.fn__str_scramble(''+dbo.fn__str_quote(c.[name],''[]'')+
         '',rand(checksum(newid()))) as [''+c.[name]+'']''
    else dbo.fn__str_quote(c.[name],''[]'')
    end
    +
    case
    when colorder=(select max(colorder) from syscolumns where id=object_id(@tbl))
    then ''''
    else '',''
    end
from syscolumns c
join systypes t
on c.xusertype=t.xusertype
where id=object_id(@tbl)
order by colorder

-- create the script to script the select of data and store into #out

insert #src(line) select ''insert #out(line) ''
insert #src(line) select ''select ''+coalesce(''top ''+@top+'' '','''')+'' '' -- '' ''''N''''''
-- insert #src(line) select ''''''insert [''+@dst+''](''+@oflds+'') ''

-- sp__Script_data ''sios_dict'',@opt=''restore''
-- sp__Script_data ''sios_dict'',@opt=''bincols:cod''

insert #src(line)
select
    ''+''+
    case
    -- if binary column
    when exists(select null from @bincols where [name]=c.[name])
    then
        ''coalesce(dbo.fn__hex(convert(varbinary(8000),''+dbo.fn__str_quote(c.[name],''[]'')+'')),''''null'''')''
    -- if numeric field
    when t.[name] like ''%int'' or t.name in (''real'',''float'',''decimal'',''numeric'')
    then
        ''coalesce(convert(nvarchar(4000),''+
        dbo.fn__str_quote(c.[name],''[]'')+
        '',126),''''null'''')''
    else
        replace(
            ''coalesce("N"""+replace(convert(nvarchar(4000),''
            +
            case
            when @scramble=1 and (t.[name] like ''%char'' or t.name in (''sysname''))
            then ''dbo.fn__str_scramble(''+dbo.fn__str_quote(c.[name],''[]'')+
              '',rand(checksum(newid())))''
            else dbo.fn__str_quote(c.[name],''[]'')
            end
            +
            '',126),"""","""""")+"""","null")'',
            ''"'',''''''''
        )
    end+
    case
    when colid= (select max(colid)
                 from syscolumns
                 where id=object_id(@tbl)
                )
    then ''''
    else ''+'''',''''''
    end
from syscolumns c
join systypes t on c.xusertype=t.xusertype
where c.id=object_id(@tbl)
and (
    -- exclude identity if opt "noinc"
    @identity is null
    or @noinc=0
    or (@noinc=1 and @identity!=c.[name])
    )
order by c.colorder
insert #src(line) select '' as line ''
insert #src(line) select ''from ''+quotename(@tbl)

if not @where is null insert #src(line) select ''where ''+@where

if @dbg=1 exec sp__print_table ''#src''
exec sp__script_compile

if (select count(*) from #out)=0 goto err_nrec

-- now #out(line) contain cvs of selected lines

update #out set line=''union select ''+line

-- break inserts into small blocks because is faster

select @n=count(*)/64+1 from #out

set identity_insert #out on

-- insert ''inserts''
insert #out(lno,line)
select (r.row-1)*640+1,''insert into [''+@dst+'']''
from dbo.fn__range(1,@n,1) r

-- insert ''insert'' fields
insert #out(lno,line)
select (r.row-1)*640+5,''    (''+@oflds+'')''
from dbo.fn__range(1,@n,1) r

set identity_insert #out off

update #out set line=''      select ''+substring(line,14,4000)
from #out o
join dbo.fn__range(1,@n,1) r
on o.lno=(r.row-1)*640+10


-- begin to out the script
exec sp__script_template ''%safety%''

if not @where is null
or not @top is null
    select @excludes=@excludes+''|where''

exec sp__script_template ''%before%'',
                         @tokens=''%dst%|%flds%|%src%'',
                         @v1=@dst,@v2=@oflds,@v3=@src,
                         @opt=''print'',
                         @excludes=@excludes
                         --,@dbg=1

exec sp__print_table ''#out''

-- mix
exec sp__script_template ''%after%'',
                         @tokens=''%dst%|%flds%|%src%'',
                         @v1=@dst,@v2=@oflds,@v3=@src,
                         @opt=''print'',
                         @excludes=@excludes
                         -- ,@dbg=1

exec sp__printf ''\nsafe_skip:''

drop table #src
drop table #out
drop table #tpl
drop table #tpl_sec
goto ret

-- =================================================================== errors ==
err_noo:    exec @ret=sp__err ''no table found'',@proc    goto ret
err_nrec:   exec @ret=sp__err ''no records found'',@proc    goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    generate inserts for @tbl
    if dest table has identity generate adeguate code

Parameters
    @tbl        name of table where select/insert data
    @where      optional filter for rows
    @top        optional top (@top) rows
    @opt        options

Options
    merge       step into a middle #tbl for mixing operations
                The ukey is defined manually
    scramble    apply scramble function to *chars fields
    noinc       skip autoincrement column
    bincols:x|y convert columns x and y and ... into binary (0x545...)
                This is necessary in some cases where a nvarchar
                filed is used as binary container, because a special
                character block the copy&paste to&from clipboard

''
select @ret=-1

ret:
return @ret
end -- sp__script_data'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_data:

-- =============================================================== sp__script_db
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_db',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090914
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_db') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_db') with nowait
        goto skip_sp__script_db
        end
    if @ver>090914
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_db') with nowait
        goto skip_sp__script_db
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_db') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_db'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_db]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090914\s.zaglio: added nocollation
    v:090909\s.zaglio: create a script of all db with dependencies, users, roles, ext.prop
    t:
        exec sp__run_cmd ''del %temp%\%@@servername%_%db_name()%.sql /q''
        exec sp__script_db
        exec sp__run_cmd ''type %temp%\%@@servername%_%db_name()%.sql''
        exec sp__run_cmd ''del %temp%\%@@servername%_%db_name()%.sql /q /f''
        -- exec sp__run_cmd ''dir %temp%\*.*''
        -- exec sp__run_cmd ''del %temp%\*.* /q /s /f''
*/
CREATE procedure [dbo].[sp__script_db]
    @srv    nvarchar(100)=null,--the name of the source server
    @db     nvarchar(100)=null,--the name of the source database
    @uid    nvarchar(100)=null,--the login to the source server
    @pwd    nvarchar(100)=null,--the password to the source server
    @out    sysname=null,
    @dbg    bit=0
as
begin
set nocount on
if @srv is null select @srv=@@servername
if @db is null select @db=db_name()
declare
    @hr int,
    @filename nvarchar(100),
    @oserver int,
    @otransfer int,
    @errorobject int,
    @strcommand nvarchar(255),
    @strresult nvarchar(255),
    @errormessage nvarchar(2000),
    @strobjectname nvarchar(100),
    @slice int

declare @tmp sysname
exec sp__get_temp_dir @tmp out

if right(@out,1)=''\'' select @out=left(@out,len(@out)-1)
if not @out is null and right(@out,4)!=''.sql'' and @out!=''#src''
    select @filename=@out
else
    set @filename = @tmp
select @filename=@filename+''\''+@srv+''_''+@db+''.sql''

if @dbg=1 exec sp__printf ''out to:%s'',@filename

exec @hr = sp_oacreate ''sqldmo.sqlserver'', @oserver out
if @hr=0 exec @hr = sp_oacreate ''sqldmo.transfer'', @otransfer out
if @pwd is null or @uid is null
    begin
    --use a trusted connection
    if @hr=0 select @errormessage=''setting login to windows authentication on ''+@srv, @errorobject=@oserver
    if @hr=0 exec @hr = sp_oasetproperty @oserver, ''loginsecure'', 1
    if @hr=0 select @errormessage=''logging in to the requested server using windows authentication on ''+@srv
    if @uid is null and @hr=0 exec @hr = sp_oamethod @oserver, ''connect'', null, @srv
    if @uid is not null and @hr=0 exec @hr = sp_oamethod @oserver, ''connect'', null, @srv ,@uid
    end
else
    begin
    if @hr=0 select @errormessage = ''connecting to ''''''+@srv+
        '''''' with user id ''''''+@uid+'''''''', @errorobject=@oserver
    if @hr=0 exec @hr = sp_oamethod @oserver, ''connect'', null, @srv ,
                         @uid , @pwd
    end
if @hr=0 select @errorobject=@otransfer,
        @errormessage=''assigning values to parameters''

if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyallobjects'', 1
if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copydata'', 0
if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyschema'', 1

--if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyalldefaults'', 0
--if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyallobjects'', 0
--if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyallrules'', 0
--if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyallstoredprocedures'', 0
--if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyalltables'', 0
--if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyalluserdefineddatatypes'', 0
--if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyalltriggers'', 0
--if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''copyallviews'', 0
--
-- if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''dropobjectsfirst'', 0
if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''includedependencies'', 1
if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''includegroups'', 1
if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''includelogins'', 1
if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''includeusers'', 1
if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''includedb'', 1

-- sqldmo_script_type vars
-- see: http://msdn.microsoft.com/en-us/library/aa225364(SQL.80).aspx
-- see: http://msdn.microsoft.com/en-us/library/aa225398(SQL.80).aspx

if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''scripttype'', 68  -- print 4|64 -- def|tofile
if @hr=0 exec @hr = sp_oasetproperty @otransfer, ''script2type'', 12582918 -- print 4194304|4|2|8388608 -- ext.prop|unicode|nocollation

select @strcommand = ''databases("'' + @db + ''").scripttransfer''

if @hr=0
    begin
    create table #devnul (t ntext) insert into #devnul -- prevent output
    exec @hr = sp_oamethod @oserver, @strcommand, null, @otransfer, 2, @filename
    drop table #devnul
    end

if @hr=0 select @errorobject=@oserver,@errormessage=''using method ''+@strcommand
if @hr<>0
    begin
    declare
        @source nvarchar(255),
        @description nvarchar(255),
        @helpfile nvarchar(255),
        @helpid int

    execute sp_oageterrorinfo  @errorobject,
        @source output,@description output,@helpfile output,@helpid output

    select @errormessage=''error whilst ''+@errormessage+'', ''+@description
    raiserror (@errormessage,16,1)
    end
exec sp_oadestroy @otransfer
exec sp_oadestroy @oserver
return @hr
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_db:

-- ============================================================ sp__script_debug
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_debug',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130927
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_debug') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_debug') with nowait
        goto skip_sp__script_debug
        end
    if @ver>130927
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_debug') with nowait
        goto skip_sp__script_debug
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_debug') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_debug'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_debug]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:fast,debug,keyuboard,shortcut,
    v:130927\s.zaglio: better automation
    v:130922\s.zaglio: enable fast debug from ssms
    t:sp__script_debug ''select @@spid as spid,@@version ver,db_name() db''
    t:sp__script_debug ''''
*/
CREATE proc sp__script_debug
    @script nvarchar(4000) = null,
    @opt sysname = null,
    @dbg int=0
as
begin -- no try because not useful with sp__err test for example
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare @test_id int,@crlf nvarchar(4),@sql nvarchar(max)
-- =========================================================== initialization ==
select @test_id=isnull(object_id(''tempdb..#test''),0),@crlf=crlf from fn__sym()
-- print object_id(''tempdb..#test'')
-- ======================================================== second params chk ==
if @script is null and @test_id=0 goto help
-- =============================================================== #tbls init ==
-- ===================================================================== body ==

if not @script is null and @test_id!=0
    begin
    drop proc #test
    exec sp__printf ''#test dropped''
    select @test_id=0
    end

if @script='''' goto ret

if @test_id!=0
    exec #test
else
    begin
    if not object_id(@script) is null select @script=''exec ''+@script
    if not object_id(left(@script,charindex('' '',@script)-1)) is null
        select @script=''exec ''+@script

    select @sql =''create proc #test''+@crlf
                +''as''+@crlf
                +''begin''+@crlf
                +''set nocount on;''+@crlf
                +@script+@crlf
                +''end''
    begin try
    exec(@sql)
    exec sp__printf ''#test created''
    exec #test
    end try
    begin catch
    exec sp__err null,''#test'',@opt=''ex''
    end catch
    end

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    create or alter or delete or execute a proc #test with body @script

Notes
    Normally I associate this sp to shortcut CTRL+9 (in memory of
    litmus test - test of nine in italian).

Parameters
    [param]     [desc]
    @opt        (not used)
    @dbg        (not used)

Examples
    -- print help
    sp__script_debug
    -- init the debug
    sp__script_debug "select @@spid as spid,@@version ver,db_name() db"
    -- run it
    sp__script_debug
    -- drop #test
    sp__script_debug ""

''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end -- proc sp__script_debug'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_debug:

-- ========================================================= sp__script_declares
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_declares',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130903
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_declares') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_declares') with nowait
        goto skip_sp__script_declares
        end
    if @ver>130903
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_declares') with nowait
        goto skip_sp__script_declares
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_declares') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_declares'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_declares]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    k:variable,table
    v:130903\s.zaglio:used fn__sql_normalize in place of fn__sql_simplify
    v:130521\s.zaglio:correction of help
    d:120508\s.zaglio:sp__script_vtable
    v:111124\s.zaglio:create declare of a variable table based on sql
*/
CREATE proc sp__script_declares
    @sql nvarchar(4000) = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @sql is null goto help

-- ============================================================== declaration ==
declare
    @tmp sysname
create table #src(lno int identity,line nvarchar(4000))

-- =========================================================== initialization ==
select
    @tmp=''#''+replace(convert(sysname,newid()),''-'',''_''),
    @sql=dbo.fn__str_unquote(@sql,''[]'')

-- ======================================================== second params chk ==
-- ===================================================================== body ==

if left(dbo.fn__sql_normalize(@sql,''sel''),7)=''select ''
    select @sql=''select top 0 * into ''+@tmp+'' from (''+@sql+'') a''
else
    if left(@sql,1)!=''#''
        begin
        if object_id(@sql) is null goto err_nob
        select @sql=''select top 0 * into ''+@tmp+'' from [''+@sql+'']''
        end
if left(@sql,1)=''#''
    begin
    if object_id(''tempdb..''+@sql) is null
        goto err_nob
    else
        select @tmp=@sql
    end

insert #src(line) select ''declare @tmp table(''
-- select * from fn__sql_def_cols(''sysobjects'','','',default)
select @sql=@sql+''
insert #src(line) select ''''    ''''+dbo.fn__flds_quotename(fld,default)+'''' ''''+def+sep from fn__sql_def_cols(''''''+@tmp+'''''','''','''',default) order by ord
''
exec(@sql)
insert #src(line) select '')''

exec sp__print_table ''#src''

drop table #src
goto ret

-- =================================================================== errors ==
err_nob: exec @ret=sp__err ''object not found'',@proc goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    script declare of a variable table
    (todo: declare variables for fields)

Parameters
    @sql    table,view,#temp table or query to use
    @opt    options
            (not used)

Examples
    sp__script_declares "sysobjects"
    sp__script_declares "select name,xtype,crdate from sysobjects"
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_vtable'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_declares:

-- ============================================================== sp__script_dep
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_dep',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110324
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_dep') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_dep') with nowait
        goto skip_sp__script_dep
        end
    if @ver>110324
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_dep') with nowait
        goto skip_sp__script_dep
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_dep') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_dep'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_dep]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:110324\s.zaglio: show also inverse list
    v:110315\s.zaglio: show dependencies of an object
    t:sp__script_dep ''sp__script_dep''
    t:sp__script_dep ''tids'',@opt=''uses''
*/
CREATE proc sp__script_dep
    @obj sysname = null,
    @level int = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
select @level=isnull(@level,1)
-- ============================================================== declaration ==
declare
    @drop bit
-- =========================================================== initialization ==
-- select -- insert before here --  @end_declare=1
-- ======================================================== second params chk ==
if @obj is null or @level>1 goto help

if object_id(''tempdb..#dep'') is null
    create table #dep(
        id int identity,
        uses bit,
        obj sysname,
        buildin sysname null,
        usr sysname null,
        comment sysname null,
        [level] int
        )
else
    select @drop=0

-- ===================================================================== body ==
-- sp__script_dep ''sp__script_dep''
if charindex(''|uses|'',@opt)=0
    insert into #dep(uses,obj,buildin,usr,comment,[level])
    select distinct
        0 as uses,
        object_name(dep.depid) as require,
        convert(sysname,tag.val1) as buildin,
        convert(sysname,val2) as usr,
        convert(sysname,val3) as comment,
        1 as [level]
    -- select top 10 * from sysdepends dep
    from sysdepends dep --sys.sql_dependencies
    cross apply fn__script_info(object_name(depid),''v'',default) tag
    where id=object_id(@obj)
    and tag.row=0
    order by 1
else
    insert into #dep(uses,obj,buildin,usr,comment,[level])
    select distinct
        1 as uses,
        object_name(dep.id) as require,
        convert(sysname,tag.val1) as buildin,
        convert(sysname,val2) as usr,
        convert(sysname,val3) as comment,
        1 as [level]
    -- select top 10 * from sysdepends dep
    from sysdepends dep --sys.sql_dependencies
    cross apply fn__script_info(object_name(depid),''v'',default) tag
    where depid=object_id(@obj)
    and tag.row=0
    order by 1

if @drop is null
    begin
    select * from #dep order by id
    drop table #dep
    end

goto ret

-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    show dependencies of an object
    If table #dep exists, will be filled to be used
    by caller:
        create table #dep(
            id int identity,
            uses bit,
            obj sysname,
            buildin sysname null,
            usr sysname null,
            comment sysname null,
            [level] int
            )

Parameters
    @obj    object name
    @level  show more levels of dependencies
            (today only 1 level is admitted)
    @opt    options
            uses    show objects that uses @obj and not that depends

Examples
    sp__script_dep ''''sp__script_dep''''
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_dep'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_dep:

-- ======================================================== sp__script_evolution
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_evolution',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130823
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_evolution') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_evolution') with nowait
        goto skip_sp__script_evolution
        end
    if @ver>130823
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_evolution') with nowait
        goto skip_sp__script_evolution
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_evolution') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_evolution'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_evolution]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:extract,data,version,history,project,chart,graphics
    v:130823\s.zaglio: extract data for a graphics of history of develop
    t:sp__script_evolution ''utility'',@dbg=1
*/
CREATE proc sp__script_evolution
    @grp sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @older_dt datetime,@newer_dt datetime,@days int

declare @tags table(obj sysname, tag varchar(8), val1 sysname,val3 sysname)
declare @nfo table(sdt sysname,dt datetime,comment nvarchar(max))
-- =========================================================== initialization ==

-- ======================================================== second params chk ==
if @grp is null goto help

-- =============================================================== #tbls init ==

-- ===================================================================== body ==
-- extract all version/release and group tags
insert @tags
select obj,tag,isnull(cast(val1 as sysname),''''),isnull(cast(val3 as sysname),'''')
from fn__Script_info(default,''rvg'',default)

-- remove objs of other groups than @grp
delete  tags
from @tags tags
join (
    select a.obj
    from @tags a
    join @tags b on a.obj=b.obj and b.tag in (''v'',''r'')
    and isnumeric(cast(b.val1 as sysname))=1
    where a.tag=''g'' and cast(a.val1 as sysname)!=@grp
    ) todel on todel.obj=tags.obj

-- remove group and not tagged obj or not valid versions
delete @tags
where tag=''g'' or tag is null or isnumeric(val1)=0
or (isnumeric(val1)=1 and val1<''070101'')

-- trim time
update @tags set val1=left(val1,6)

if @dbg>0
    select *,isnumeric(val1) isnum
    from @tags
    order by isnumeric(val1) desc,val1

-- calculate ranges
select
    @older_dt=convert(datetime,min(val1)),
    @newer_dt=convert(datetime,max(val1))
from @tags
select @days=datediff(d,@older_dt,@newer_dt)+1

-- group info
begin try
insert @nfo
select
    val1 as sdt,
    convert(datetime,val1,12) dt,
    ((select distinct obj+'':''+val3+'';''
      from @tags
      where val1=a.val1
      for xml path(''''),type).value(''.'',''nvarchar(max)''
    ))
from (select distinct val1 from @tags) a
end try
begin catch
select ''bad date'' nfo,* from @tags where isdate(val1)=0
goto ret
end catch

if @dbg>0 select ''@nfo'' [@nfo],* from @nfo
-- sp__script_evolution ''utility'',@dbg=1

-- show grouped history info
select
    cast(y as sysname)+''-''+
    substring(''jan feb mar apr may jun jul aug sep oct nov dec '',(m*4)-3,3) dt,
    sum(changes) changes,
    (select distinct comment
     from @nfo
     where year(dt)=y and month(dt)=m
     for xml path(''''),type).value(''.'',''nvarchar(max)''
    ) comments
from (
    select year(b.dt) y,month(b.dt) m,isnull(changes,0) changes
    from (
        select dt,count(*) changes
        from @nfo
        --order by convert(datetime,sdt,112)
        group by dt
        ) a
    right join (
        select @older_dt+row-1 as dt
        from fn__range(1,@days,1)
        ) b
    on a.dt=b.dt
    ) a
group by y,m
order by y,m

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    extract data for a graphics of history of develop;
    can be used into Excel

Parameters
    [param]     [desc]
    @opt        options
    @grp        group name
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_evolution'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_evolution:

-- ============================================================ sp__script_fkeys
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_fkeys',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120117
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_fkeys') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_fkeys') with nowait
        goto skip_sp__script_fkeys
        end
    if @ver>120117
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_fkeys') with nowait
        goto skip_sp__script_fkeys
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_fkeys') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_fkeys'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_fkeys]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120117\s.zaglio: a small bug
    v:110406\s.zaglio: adapted to new form, removed #objs
    v:100919\s.zaglio: adapted to #objs
    v:100328\s.zaglio: adapted to new sp__script
    v:090813\s.zaglio: get fkeys script source
    s:http://blog.sqlauthority.com/2008/04/18/sql-server-generate-foreign-key-scripts-for-database/
    t:
        create table test_fk1(id int primary key, v sysname)
        create table test_fk2(ref_id int, v sysname)
        alter table dbo.test_fk2 add constraint fk_test_fk2_test_fk1
            foreign key(ref_id) references dbo.test_fk1(id) on update cascade
            on delete no action
        exec sp__script_fkeys ''test_fk1''
        exec sp__script_fkeys ''test_fk2''
        drop table test_fk2
        drop table test_fk1
    t:sp__script_fkeys ''%''
*/
CREATE procedure [dbo].[sp__script_fkeys]
    @obj sysname=null,
    @opt sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0,
       @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- 090813\s.zaglio: get fkeys script source
if @obj is null goto help
/*
author : seenivasan
this procedure is used for generating foreign key script.
*/

declare @src table (lno int identity primary key,line nvarchar(4000))

declare @fkname nvarchar(128)
declare @fkcolumnname nvarchar(128)
declare @pkcolumnname nvarchar(128)
declare @fupdaterule int
declare @fdeleterule int
declare @fieldnames nvarchar(500)
declare @n int

create table #temp(
    pktable_qualifier nvarchar(128),
    pktable_owner nvarchar(128),
    pktable_name nvarchar(128),
    pkcolumn_name nvarchar(128),
    fktable_qualifier nvarchar(128),
    fktable_owner nvarchar(128),
    fktable_name nvarchar(128),
    fkcolumn_name nvarchar(128),
    key_seq int,
    update_rule int,
    delete_rule int,
    fk_name nvarchar(128),
    pk_name nvarchar(128),
    deferrability int)

declare @objs table (obj sysname)

if charindex(''%'',@obj)>0
    insert @objs select [name]
    from sysobjects
    where [name] like @obj
    and xtype=''u''
else
    insert @objs select parsename(@obj,1)

declare ttablenames cursor local for
    select obj
    from @objs

open ttablenames

fetch next
from ttablenames
into @obj

while @@fetch_status = 0
    begin
    insert #temp
    exec dbo.sp_fkeys @obj
    fetch next
    from ttablenames
    into @obj
    end -- while
close ttablenames
deallocate ttablenames

set @fieldnames = ''''
set @obj = ''''
select distinct
    fk_name as fkname,fktable_name as ftname,
    @fieldnames as ftfields,pktable_name as stname,
    @fieldnames as stfields,@fieldnames as fktype
into #temp1
from #temp
order by fk_name,fktable_name,pktable_name

declare fk_cusror cursor for
select distinct fkname from #temp1

open fk_cusror
fetch
from fk_cusror into @fkname

while @@fetch_status = 0
    begin

    declare fk_fields_cusror cursor for
    select fkcolumn_name,pkcolumn_name,update_rule,delete_rule
    from #temp
    where fk_name = @fkname
    order by key_seq

    open fk_fields_cusror
    fetch
    from fk_fields_cusror into  @fkcolumnname,@pkcolumnname,
                                @fupdaterule,@fdeleterule
    while @@fetch_status = 0
        begin

        update #temp1 set ftfields =
        case when len(isnull(ftfields,'''')) = 0
             then ''[''+@fkcolumnname+'']''
             else ftfields+'',[''+@fkcolumnname+'']''
             end
        where fkname = @fkname

        update #temp1 set stfields = case when len(isnull(stfields,''''))
        = 0 then ''[''+@pkcolumnname+'']''
        else stfields
        +'',[''+@pkcolumnname+'']'' end
        where fkname = @fkname

        fetch next
        from fk_fields_cusror into  @fkcolumnname,@pkcolumnname,
                                    @fupdaterule,@fdeleterule
        end -- fk_fields

    update #temp1 set fktype = case when @fupdaterule = 0
    then fktype + '' on update cascade''
    else fktype end
    where fkname = @fkname

    update #temp1 set fktype = case when @fdeleterule = 0
    then fktype + '' on delete cascade''
    else fktype end
    where fkname = @fkname

    close fk_fields_cusror
    deallocate fk_fields_cusror

    fetch next
    from fk_cusror into @fkname
    end -- fk_cursor

close fk_cusror
deallocate fk_cusror

if exists(select null from #temp1) insert into @src(line) select ''go''

insert into @src(line)
select ''alter table [dbo].[''+ftname+''] with nocheck add constraint [''+fkname+''] foreign key (''+ftfields+'')
references [''+stname+''] (''+stfields+'') ''+fktype
from #temp1

if object_id(''tempdb..#src'') is null
    select line from @src order by lno
else
    insert #src(line)
    select line from @src order by lno

goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc
select @ret=-1

ret:
set nocount off
return @ret
end -- sp__Script_fkeys'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_fkeys:

-- ============================================================ sp__script_group
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_group',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131126
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_group') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_group') with nowait
        goto skip_sp__script_group
        end
    if @ver>131126
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_group') with nowait
        goto skip_sp__script_group
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_group') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_group'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_group]

begin try
exec dbo.sp_executesql @statement = N'/*  Leave this unchanged doe MS compatibility
    l:see LICENSE file
    g:utility,script
    v:131126\s.zaglio: added recache option
    v:131027\s.zaglio: added cache for slow computers
    v:130730.1230,130729,130726,130725,130724,130723,130722,130719\s.zaglio: restructuring
    r:130703;130701\s.zaglio: removed prefix; removed old unused code;moving code sp__script
    r:130613\s.zaglio: deprecated html out and moved index to ...
    r:130610\s.zaglio: integration with fn__script_group_select
    v:130417;130127\s.zaglio: added author filter and script of obj|obj|...;added %latests_objs%
    v:121229;121218\s.zaglio: replaced %now% with %dt% and added %db%;around problem of "\"
    v:121108\s.zaglio: searching bug of \{space} (found into sp__script)
    v:121004.1614\s.zaglio: exclude _old objs and used sp__deprecated
    v:120924;120921\s.zaglio: around error management;added prefix opt
    v:120920\s.zaglio: exec setup also at end and used try-catch
    v:120907.1500\s.zaglio: deprecated tag X and used postfix _%grp%_setup
    r:120907\s.zaglio: added chk if sp__script_store,grp,%_setup are in R
    v:120827\s.zaglio: added app_name test and forced convert to db 90
    v:120823.1000;120731\s.zaglio: added nochk option and better tests;test of #script_results
    v:120727\s.zaglio: added errors check and script repeat
    v:120725.1557;120724\s.zaglio: spring test and done;messaging and new tag X
    v:120516.1841\s.zaglio: adapted to new fn__script_sysobjs & fn__script_sign
    v:120510;120201\s.zaglio: a bug near deprecated "null" names;fn__sym before others
    v:111230;111229\s.zaglio: adapted to new sp__script_template;adapting to new sp__script_template
    r:111228;111223\s.zaglio: added support for multi &grp;adapting to new sp__script_template
    v:111205;111111\s.zaglio: added script of direct objects;excluded deprecated from group
    v:111007;110916\s.zaglio: added sortable func to index.html;adapted to new fn__buildin
    v:110824;110704\s.zaglio: adapted to new sp__script_template;specified log_ddl upgrade version
    v:110701.1848;110630;110629\s.zaglio: added upgrade of log_ddl;re-profiling;used fn__sysobjects
    v:110628\s.zaglio: added deprecated management and script of trigger db
    v:110623;110621\s.zaglio: better comment position;a small bug near drop and more info
    v:110620;110615\s.zaglio: done 3rd review with versioning;readded out to file/s and html
    r:110614;110603\s.zaglio: added versioning;added integration with history
    v:110509;110406\s.zaglio: added print obj in regen...;adapted to new sp__script & C.
    v:100919.1005\s.zaglio: a bug near html creations
    v:100919.1001\s.zaglio: script ordered by name desc
    v:100919\s.zaglio: added note about script of fkeys
    v:100905;100724\s.zaglio: added origin db check;a bug near html generation
    v:100718100612\s.zaglio: added check of origin db;added more examples
    v:100523;100501\s.zaglio: renamed option hidx to index;added last chg column on html table
    v:100418.2200\s.zaglio: use of fn__script_info and added hidx
    v:100411\s.zaglio: added * to collect tables&views
    v:100405;100403\s.zaglio: done & tested;adapted to 3rd remake of sp__script
    v:100328;091126\s.zaglio: adapted to new version of sp__Script;added list of groups in help
    v:091018\s.zaglio: remake of old sp__script_group to separate from use of table
    t:sp__script_group ''UTILITY@s.zaglio'',@dbg=2,@exclude=''fn__word%''
    t:sp__script_group ''utility'',@out=''%temp%\%grp%_%t'',@dbg=2  -- single file out
    t:sp__script_group ''utility'',@out=''%temp%\%obj%_%t'',@dbg=2  -- multi file out
    t:sp__script_group ''fn__str_table|fn__str_at''
    t:sp_script_utility @out=''%temp%\%grp%.sql'',@dbg=1
*/
CREATE proc [dbo].[sp__script_group]
    @grp        nvarchar(4000) = null,
    @out        nvarchar(max)  = null out,
    @exclude    nvarchar(4000) = null,
    @include    nvarchar(4000) = null,
    @opt        sysname = null,
    @dbg        int     = 0
as
begin try
set nocount on

declare
    @proc sysname,@ret int,
    @e_msg nvarchar(4000),@e_p1 sysname,@e_p2 sysname,
    @t datetime,@i int,@aut sysname -- derived parameter

select @proc=object_name(@@procid),@ret=0,
       @opt=dbo.fn__str_quote(coalesce(@opt,''''),''|''),
       @t=getdate()

-- ================================================================ param chk ==

if @grp is null goto help

-- ============================================================= declarations ==

declare
    @n int,@obj sysname,@sql nvarchar(max),@isql nvarchar(4000),
    @src nvarchar(max),
    @type nvarchar(2),@tmp sysname,@var_id int,@src_id int,
    @db sysname,@xt nvarchar(2),
    @go sysname,@ver sysname,
    @dothtm nvarchar(32),@crlf nvarchar(2),
    @grp_sep nvarchar(4),@obj_sep nvarchar(4),
    @grp_util sysname,@db_util sysname,
    @tgs varchar(4),                    -- tags of objs to consider,
    @tag nvarchar(8),
    @tpl sysname,                       -- name of template
    @ver_grp numeric(10,4),             -- max version of utilities
    @latests_objs nvarchar(4000),       -- max ver:obj,obj,obj,...
    @grp_setup sysname,                 -- like for name of group setup object
    @drop nvarchar(512),
    @opt_script sysname,
    @tmp_script_group_cache_id int,
    @key varbinary(16),
    -- options
    @bin bit,@recache bit,
    @end_declare bit

if charindex(''|'',@grp)>0 goto script_objs

declare @objs table(
    obj_id int null,
    obj sysname,
    xt nvarchar(2) null,
    tag nvarchar(16) default(''v''),
    aut sysname null,           -- info.val2
    ver nvarchar(16) null,      -- info.val1
    [des] sysname null,         -- info.val3
    grp1 sysname null default(''''),
    grp2 sysname null default(''''),
    grp3 sysname null default(''''),
    core bit default(0),        -- basic obj used to define others
    [drop] nvarchar(512) null,
    ord int,
    match bit
    )

-- =========================================================== check in cache ==

-- drop table tmp_script_group_cache
if object_id(''tmp_script_group_cache'') is null
    create table tmp_script_group_cache(
        dt datetime not null,
        id int identity not null,
        [key] varbinary(16) not null,
        latests_objs nvarchar(320) not null,   -- print 450-128=322
        [out] nvarchar(max) not null,
        constraint pk_tmp_script_group primary key (id)
        )

select @key=hashbytes(''md5'',@grp+''|''+isnull(@include,'''')
                                +''|''+isnull(@exclude,'''')+
                                +@opt)

select @tmp_script_group_cache_id=isnull(object_id(''tmp_script_group_cache''),0)

if @tmp_script_group_cache_id!=0
    -- purge data older than one year (not necessary but more safest)
    delete from tmp_script_group_cache where dt<getdate()-365

-- =========================================== exclude objs managed elsewhere ==

select @i=charindex(''@'',@grp)
if @i>0 select @aut=substring(@grp,@i+1,128),@grp=left(@grp,@i-1)

insert into @objs(
    obj_id,obj,xt,tag,aut,ver,[des],grp1,grp2,grp3,core,[drop],ord,match
    )
select
    obj_id,obj,xt,tag,aut,ver,[des],grp1,grp2,grp3,core,[drop],ord,match
from fn__script_group_select(@grp,@exclude,@include,default,default)

if @@rowcount=0 raiserror(''no objects for this group'',16,1)

-- check for bad headers
select @exclude=null
select @exclude=isnull(@exclude+'','','''')+obj
from (select distinct obj from @objs where isnumeric(ver)=0) a
if isnull(@exclude,'''')!=''''
    raiserror(''this objects has bad version: %s'',16,1,@exclude)

-- update version for non versioned objects
update @objs set ver=dbo.fn__script_buildin(getdate(),1,'''','''')
where ver is null -- teorically tables

if @dbg>0 exec sp__elapsed @t out,''after obj list''

if @dbg>1 select * from @objs order by core desc,tag,ord,obj

if @dbg=0
and exists(
    select top 1 null
    from @objs nfo
    where cast(tag as nchar)=''R''
    and (
        object_name(obj_id) in (@proc,''sp__script_store'',''sp__script'')
        or object_name(obj_id) like ''%''+@grp+''_setup''
        )
    )
    begin
    select @e_msg=''"sp__script_group" or "sp__script_store" ''
                         +''or "sp__script" ''
                         +''or "group setup object" are in R state''
    raiserror(@e_msg,16,1)
    end

-- ============================================================== #temp table ==
select
    @var_id=isnull(object_id(''tempdb..#vars''),0),
    @src_id=isnull(object_id(''tempdb..#src''),0)

create table #tpl (lno int identity,line nvarchar(4000))
create table #tpl_sec(lno int identity,section sysname,line nvarchar(4000))
create table #tpl_cpl(tpl binary(20),section sysname,y1 int,y2 int)
create index #ix_tpl_cpl on #tpl_cpl(tpl,section)
if @var_id=0 create table #vars (id nvarchar(16),value nvarchar(4000))
if @src_id=0 create table #src (lno int identity,line nvarchar(4000))

-- todo: test run, checking if is in R state by other user

-- inside dbg
/*
exec(''
create proc #chk as
if exists(select null from #src where right(line,2)=''''\ '''')
    exec sp__printf ''''bug!!!''''
'')
*/

-- ===============================================================  templates ==
-- NB: chk of version of utility is limitating if outside
--     better use fn__Script_sign inside sp.

/*
    templates dependencies
    - %scr_header%
        - %disable_tracer%
        - %tmp_deprecated%
        - for each core object
            - %obj_core_chk%
            - %skip_obj%
        - for each other objects
            - %obj_ver_chk% -- from sp__script
            - %skip_obj%
        - %enable_tracer%
    - %scr_footer%
*/

exec sp__script_templates ''group''
if @dbg>0 exec sp__elapsed @t out,''after templates init''

-- ===================================================================== init ==

exec sp__get_temp_dir @tmp out

select
    -- @out=nullif(@out,''''),    because used to return the entire script
    @grp_setup=(select top 1 name
                from sys.objects
                where type=''P'' and name like ''%''+@grp+''_setup''
               ),
    @grp_util=''utility'',
    @db_util=''utility'',
    @tgs=''rv'',
    @grp_sep='','',
    @obj_sep=''|'',
    @crlf=crlf,
    @db=db_name(),
    @go=''go'',
    @dothtm=''.htm'',
    @bin=charindex(''|bin|'',@opt),
    @recache=charindex(''|recache|'',@opt),
    @out=replace(
            replace(
                replace(@out,''%temp%'',@tmp),
                ''%grp%'',replace(@grp,'','',''_'')
                ),
            ''%t'',dbo.fn__format(getdate(),''YYYYMMDD_HHMMSS'',default)
            )
        +case when @out='''' or @out like ''%.sql'' then '''' else ''.sql'' end
from fn__sym()

-- check that every R,V tag has a correct numeric version
select @latests_objs=null
-- list of bad versions
select @latests_objs=isnull(@latests_objs+@crlf+obj+'':''+ver,
                            obj+'':''+ver
                            )
from @objs
where tag in (''r'',''v'')
and not (ver like ''[0-9][0-9][0-9][0-9][0-9][0-9]'' -- yymmdd or --yymmdd.hhmm
         or ver like ''[0-9][0-9][0-9][0-9][0-9][0-9].[0-9][0-9][0-9][0-9]'')

if not @latests_objs is null
    begin
    exec sp__printf ''%s'',@latests_objs
    raiserror(''above list of objects has bad version'',16,1)
    end

-- this is necessary due a bug or an optimization in the engine
declare @vers table(obj sysname,ver numeric(10,4))
insert @vers(obj,ver)
select obj,cast(ver as numeric(10,4)) -- this in the where causes error
from @objs
where tag in (''r'',''v'')

-- calculate max group version
select @ver_grp=max(cast(ver as numeric(10,4))) from @vers

-- sp__script_group ''utility''
-- list of latests objects
select @latests_objs=isnull(@latests_objs+'',''+obj,
                            replace(cast(@ver_grp as sysname),
                                    ''.0000'',
                                    ''''
                                   )+'':''+obj
                            )
from @vers
where ver=@ver_grp

-- ================================================ check if already in cache ==

if @tmp_script_group_cache_id!=0 and @recache=0
    begin
    select @src=null
    select top 1 @src=[out]
    from tmp_script_group_cache
    where [key]=@key and latests_objs=@latests_objs

    if not @src is null
        begin
        -- resplit into #src
        insert #src(line) select line from fn__ntext_to_lines(@src,0)
        if @out='''' select @out=@src
        if @dbg>0 exec sp__printf ''-- get script from cache''
        goto out_to_cached
        end -- test cache
    end -- if cache enabled

-- =============================================================== init macro ==

insert #vars
select ''%svr%'',@@servername union
select ''%grp%'',@grp union
select ''%grp_setup%'',@grp_setup union
select ''%db_util%'',@db_util union
select ''%latests_objs%'',@latests_objs union
 -- markers
select ''%obj%'',null union
select ''%ver%'',null union
select ''%aut%'',null union
select ''%drop%'',null

-- ======================================================== select all object ==

-- check invalid deprecated tags
if exists(
    select top 1 null
    from @objs nfo
    where nfo.tag=''d''
    and isnumeric(convert(sysname,grp1))=0
    )
    exec sp__printf ''-- W A R N I N G : some invalid deprecated tags''

-- =================================================================== header ==

if @grp!=@db_util
    select @exclude=''utility''
else
    select @exclude=''other''

-- expand proc into section
exec sp__script_template ''%script_catch_definition%'',
                         ''%script_catch_implementation%''
update #tpl_sec set line=replace(line,'''''''','''''''''''')

exec sp__script_template ''%scr_header%'',@excludes=@exclude

-- ============================================================= uninst trace ==
if @grp in (@grp_util) exec sp__script_template ''%disable_tracer%''

if @dbg>0 exec sp__elapsed @t out,''after header scripting''

-- =================================================== loop into core objects ==
select @opt_script=''upgrade|nodecl|nohdr|nofot''
if not @out is null select @opt_script=@opt_script+''|tofile''

if @grp in (@grp_util)
    begin

    exec sp__printframe ''core objects has different version check'',@out=''#src''

    declare cs_core cursor local fast_forward for
        select grp.obj,grp.xt,grp.tag,grp.[drop]
        from @objs grp
        where grp.core=1
        and grp.tag in (''r'',''v'')
        order by grp.ord,grp.obj

    open cs_core
    while 1=1
        begin
        fetch next from cs_core into @obj,@xt,@tag,@drop
        if @@fetch_status!=0 break

        select @ver=dbo.fn__script_sign(@obj,1)
        update #vars set
            value=case id
                  when ''%obj%'' then @obj
                  when ''%ver%'' then @ver
                  when ''%drop%'' then @drop
                  end
        where id in (''%obj%'',''%ver%'',''%drop%'')

        insert into #src(line)  select '''' union
                                select ''-- ''+dbo.fn__format(@obj,''=< '',77)

        if @obj!=''fn__script_sign''
            exec sp__script_template ''%obj_core_chk%''
        else
            begin
            insert #src(line)
            select ''raiserror(''''drop&create "fn__script_sign"'''',10,1)''
            insert #src(line)
            select token
            from dbo.fn__str_table_fast(@drop,@crlf)
            end

        if @dbg=1 exec sp__printf ''scripting:%s, ver:%s'',@obj,@ver

        -- ##########################
        -- ##
        -- ## dump CORE obj into #src
        -- ##
        -- ########################################################
        exec sp__script @obj,@opt=@opt_script

        if @obj!=''fn__script_sign''
            exec sp__script_template ''%skip_obj%'',@excludes=''setup''

        end -- while
        close cs_core
        deallocate cs_core

    end -- script 1st core objects for utility

if @dbg>0 exec sp__elapsed @t out,''after core scripting''

-- =============================================================== deprecated ==

exec sp__printframe ''remove deprecated'',@out=''#src''

insert into #src(line)
select ''exec sp__deprecate ''''''+[des]+'''''',''+ver
from @objs
where tag in (''d'')
order by case xt when ''TD'' then 0 else 1 end,
         ord,obj

if @dbg>0 exec sp__elapsed @t out,''after deprecated scripting''

-- ================================================== loop into objects group ==

exec sp__printframe ''group objects'',@out=''#src''

select @opt_Script=''upgrade|nodecl|nofot''
if not @out is null select @opt_script=@opt_script+''|tofile''

declare cs cursor local fast_forward for
    select grp.obj,grp.xt,grp.tag,grp.ver,grp.aut,grp.[drop]
    from @objs grp
    where tag in (''r'',''v'')
    and grp.core=0
    order by grp.ord,grp.obj

open cs
while 1=1
    begin
    fetch next from cs into @obj,@xt,@tag,@ver,@aut,@drop
    if @@fetch_status!=0 break

    update #vars set
        value=case id
              when ''%obj%'' then @obj
              when ''%ver%'' then @ver
              when ''%aut%'' then @aut
              when ''%drop%'' then @drop
              end
    where id in (''%obj%'',''%ver%'',''%aut%'',''%drop%'')

    insert into #src(line) select ''-- ''+dbo.fn__format(@obj,''=< '',77)

    if @dbg>0 exec sp__printf ''scripting:%s, ver:%s, aut:%s'',@obj,@ver,@aut
    -- ##########################
    -- ##
    -- ## dump group obj into #src
    -- ##
    -- ########################################################
    exec sp__script @obj,@opt=@opt_Script
    -- exec #chk

    if @xt=''u'' exec sp__script_fkeys @obj

    -- grp obj footer
    select @tmp=case when @obj=@grp_setup then null else ''setup'' end
    exec sp__script_template ''%skip_obj%'',@excludes=@tmp

    end -- while object
close cs
deallocate cs

if @dbg>0 exec sp__elapsed @t out,''after obj scripting''

-- ============================================================= uninst trace ==
if @grp in (@grp_util) exec sp__script_template ''%enable_tracer%''

-- =================================================================== footer ==

if @grp_setup is null select @exclude=isnull(@exclude,'''')+''setup''
exec sp__script_template ''%scr_footer%'',
                         @tokens=''%grp_setup%'',@v1=@grp_setup,
                         @excludes=@exclude

if @dbg>0 exec sp__elapsed @t out,''after footer scripting''

-- ======================================================= out to single file ==

out_to:

if @bin=1 exec sp__script_compress

select @src=
 stuff(
    (select @crlf + line
    from #src
    order by lno
    for xml path(''''), type
    ).value(''(./text())[1]'',''nvarchar(max)'')
  , 1, len(@crlf), '''')

-- if cache enabled
if @tmp_script_group_cache_id!=0
    begin
    update tmp_script_group_cache set [out]=@src
    where [key]=@key
    if @@rowcount=0
        insert tmp_script_group_cache(dt,[key],latests_objs,[out])
        select getdate(),@key,@latests_objs,@src
    end

out_to_cached:

if @src_id=0
    begin
    if @out is null
        exec sp__print_table ''#src''
    else
        begin
        if @dbg>0 exec sp__printf ''out to %s'',@out
        if @dbg=2
            begin
            exec sp__print_table ''#src''
            end
        else
            exec sp__file_write_stream @out
        if @dbg>0 exec sp__elapsed @t out,''after out to file''
        end
    end -- out

-- out to variable
if @out='''' select @out=@src

dispose:
if @src_id=0 drop table #src
if @var_id=0 drop table #vars

goto ret

-- ==================================================== script direct objects ==

script_objs:
-- t:sp__script_group ''fn_str_at|fn__str_at|sp__chknulls''
-- t:sp__script_group ''fn__str_at|sp__chknulls''
declare cs cursor local for
    select token
    from dbo.fn__str_table(@grp,''|'')
open cs
while 1=1
    begin
    fetch next from cs into @obj
    if @@fetch_status!=0 break

    exec @ret=sp__script @obj,@opt=''upgrade''
    if @ret!=0 goto ret

    end -- cursor cs
close cs
deallocate cs

goto out_to

-- =================================================================== errors ==
err:        exec @ret=sp__err @e_msg,@proc                              goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    script a group of objects by category
    see below for more info

Parameters
    #src    (optional) fill the caller table
    #vars   (optional) used to fill some specials macro (as %license%)
    @grp    single group to script can postfix with @AUTHOR
            to filter only objects where author of last version is AUTHOR;
            * the group is identified by tag G:
            * tag G support multiple groups separated by comma (",")
            * if contain a %, select names instead of group
            * if contain *, select also tables (with idxs,fkeys,trs)
            * SYS group, script S objects from fn__script_sysobjs
            * obj1|obj2|... direct script only obj1,obj2,...
              calling sp__script with opt upgrade

    @out    * if passed an empty string, return the script
            * can be a path where out a single file
              (extension is .sql)
              %grp% will be replaced with group name and create a unique file
              %t will be replaced with YYMMDD_HHMMSS
              %temp% will be replaced with windows user temp directory

    @opt    options
            bin         return results as compressed binary string
            recache     ignore cached version and re-script then re-cache

    @dbg    1 list selected objects and exit
            2 print and do not out to files

    @exclude is a "like" expression for post exclusion of objects
    @include is a "like" expression for pre inclusion of objects

Notes
    * an object that end with group name and "_setup", is considered the setup
      store procedure of the group, scripted 1st and executed immediatelly
    * if sp__script_store and sp__script_group are in R tag, cannot script
    * if object "%_%groupname%_setup" is in R, scripting is aborted

Examples
    -- normal use
    exec sp__script_group ''''utility''''

    -- replace macros
    create table #vars (id nvarchar(16),value nvarchar(4000))
    insert #vars select ''''%license%'''',''''test replacements''''
    exec sp__script_group ''''script''''
    drop table #vars

##########################
##
## SCRIPT GROUP STEPS
## ==================
##
## if script to console or into single file:
##   drop deprecated objects
##   check version
##     drop object
##     re-create
##
## is script "utility" group:
##   if exists uninstall db trigger
##   eventually re-create scripting core objects
##   drop deprecated objects
##   check version
##     drop object
##     re-create
##   if exists install db trigger and upgrade if necessary
##
########################################################

''
select val1 as grp
from fn__script_info(null,''g'',0)
where not val1 is null
union
select val2
from fn__script_info(null,''g'',0)
where not val2 is null
union
select val3
from fn__script_info(null,''g'',0)
where not val3 is null
order by 1

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_group'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_group:

-- ===================================================== sp__script_group_tofile
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_group_tofile',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=151107
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_group_tofile') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_group_tofile') with nowait
        goto skip_sp__script_group_tofile
        end
    if @ver>151107
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_group_tofile') with nowait
        goto skip_sp__script_group_tofile
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_group_tofile') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_group_tofile'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_group_tofile]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:each,object,single,file,one,into
    v:151107\s.zaglio: better dbg info
    v:130903\s.zaglio: renamed group list to index.txt
    r:130902\s.zaglio: added group list
    r:130830,130829,130824\s.zaglio: script each object of a group into file
    d:130824\s.zaglio:sp__script_tofile
    t:sp__script_group_tofile ''utility@s.zaglio'',''%temp%\utility'',@dbg=1
    t:sp__script_group_tofile ''sp__script_declares'',''%temp%\utility'',@dbg=1
*/
CREATE proc sp__script_group_tofile
    @grp nvarchar(4000) = null,
    @out nvarchar(1024) = null,
    @exclude nvarchar(4000) = null,
    @include nvarchar(4000) = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @i int,@aut sysname,@src_id int,@obj sysname,@xt nvarchar(2),
    @tag nvarchar(8),@ver sysname,@drop nvarchar(512),@ext sysname,
    @temp nvarchar(512),@path nvarchar(1024),@n int,
    @psep nchar(1),@cmd nvarchar(1024),@pext int,@pdir int,
    @fmt sysname,@list_filename nvarchar(1024),@cr nchar(1),@lf nchar(1),
    -- option
    @lo bit,
    @end_declare bit

if @src_id is null
    create table #src (lno int identity,line nvarchar(4000))

-- =========================================================== initialization ==
exec sp__get_temp_dir @temp out
select
    @psep=psep,
    @grp=nullif(@grp,''''),
    @ext=''.sql'',
    @out=nullif(replace(@out,''%temp%'',@temp),''''),
    @cr=cr,@lf=lf,
    -- options
    @lo=charindex(''|lo|'',@opt)
from fn__sym()

-- ======================================================== second params chk ==
if @grp is null goto help

select
    @pext=dbo.fn__charindex(''.'',@out,-1),
    @pdir=dbo.fn__charindex(@psep,@out,-1)

if @pext>0 select @ext=''''
if @pext>@pdir raiserror(''@out must be a path ending with %s'',16,1,@psep)
if right(@out,1)!=@psep select @out=@out+''\''

-- ========================================================= get objects list ==
-- normalize objects list if given
select @grp=replace(replace(@grp,@lf,@cr),@cr+@cr,@cr)
select @grp=replace(@grp,@cr,''|'')

-- extract author to filter, if given
select @i=charindex(''@'',@grp)
if @i>0 select @aut=substring(@grp,@i+1,128),@grp=left(@grp,@i-1)

-- =============================================================== #tbls init ==
select top  0 * into #objs_list
from fn__script_group_select(default,default,default,default,default)

-- temp table for index file
select top 0
    -- identity(int,1,1) lno,
    obj,tag,ver,aut,des
into #group_file_list
from #objs_list

-- ===================================================================== body ==

insert into #objs_list
select *
from fn__script_group_select(@grp,@exclude,@include,''@''+@aut,default)

-- if no grp objects, test for single object
if @@rowcount=0 and not object_id(@grp) is null
    begin
    if @dbg>0 exec sp__printf ''adding single object, keeping directory''
    insert into #objs_list
    select g.*
    from fn__script_group_select(default,@exclude,@include,''@''+@aut,default) g
    cross apply fn__str_table_fast(@grp,''|'') objs
    where obj like objs.token
    end
else    -- found grp''s objects
    begin
    -- register info about group (now is in the name:@grp.csv)
    -- insert #group_file_list(obj,tag,ver,aut,des) select @grp,'''','''','''',''''
    -- drop target directory if exists
    if not @out is null
        begin
        select @cmd=''rmdir /s /q "''+@out+''"''
        exec xp_cmdshell @cmd,no_output
        end
    end

if not @out is null
    begin
    select @list_filename=@out+@grp+''.csv''
    -- create target directory
    select @cmd=''mkdir "''+@out+''"''
    exec xp_cmdshell @cmd,no_output
    end

-- ======================================================= create/update list ==
-- reload file if exists
if not @out is null
    begin
    if @dbg>0 exec sp__printf ''list to: %s'',@list_filename

    exec xp_fileexist @list_filename, @i output
    if @i!=0 exec sp__csv_import @list_filename,''#group_file_list'',@opt=''noh''

    if @dbg>0 select @n=count(*) from #group_file_list
    end

-- update/add released/versioned
update gl set ver=ol.ver, aut=ol.aut, des=ol.des
from #group_file_list gl join #objs_list ol
on gl.obj=ol.obj and gl.tag=ol.tag
if @dbg>0 select @n=@@rowcount

insert #group_file_list(obj,tag,ver,aut,des)
select ol.obj,ol.tag,ol.ver,ol.aut,ol.des
from #group_file_list gl right join #objs_list ol
on gl.obj=ol.obj and gl.tag=ol.tag
where ol.tag in (''r'',''v'') and gl.obj is null
if @dbg>0 select @n=@@rowcount

-- update/add deprecated
update gl set ver=ol.ver, aut=ol.aut, des=ol.des
from #objs_list ol
join #group_file_list gl on gl.obj=ol.des and gl.tag=ol.tag
where ol.tag in (''d'')
if @dbg>0 select @n=@@rowcount

insert #group_file_list(obj,tag,ver,aut,des)
select distinct ol.des,ol.tag,ol.ver,ol.aut,''in ''+ol.obj
from #objs_list ol
left join #group_file_list gl on gl.obj=ol.des and gl.tag=ol.tag
where ol.tag in (''d'') and gl.obj is null
if @dbg>0 select @n=@@rowcount

-- print list if no to out-put
if @out is null
    begin
    exec sp__select_astext ''#group_file_list''
    goto ret
    end

-- save list to file
if 0=(select count(*) from #group_file_list)
    raiserror(''generating group file list'',16,1)

exec sp__csv_export ''#group_file_list'',@list_filename,@opt=''noh''

if @lo=1 goto dispose

-- ========================================= loop into list, script and store ==

declare cs cursor local fast_forward for
    select grp.obj,grp.xt,grp.tag,grp.ver,grp.aut,grp.[drop]
    from #objs_list grp
    where tag in (''r'',''v'')
    order by grp.ord,grp.obj

open cs
while 1=1
    begin
    fetch next from cs into @obj,@xt,@tag,@ver,@aut,@drop
    if @@fetch_status!=0 break

    -- refine path
    select @path=@out+@obj+@ext

    if @dbg>1
        exec sp__printf ''to script obj:%s(%s) ver:%s aut:%s'',@obj,@xt,@ver,@aut
    else
        begin
        truncate table #src
        exec @ret=sp__script @obj
        select @n=count(*) from #src
        if @dbg>0
            exec sp__printf ''scripting obj:%s(%s) ver:%s aut:%s lines:%d to:%s'',
                            @obj,@xt,@ver,@aut,@n,@path
        if @ret=0 exec @ret=sp__file_write_stream @path,@fmt=@fmt
        if @ret!=0 goto ret -- sp has written its error
        end

    end -- while
    close cs
    deallocate cs

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    script each object of a group into a single file

Notes
    - a file index.txt is created and populated with the list of
      versions in CSV format:
            obj,tag,version,author,last comment

    - when single objects are scripted, the content of directory is kept and
      the list updated

Parameters
    [param]     [desc]
    @grp        group name with optional prefix @author or list of objects
                separated by | or CR/LF
    @out        destination path (a directory where put obj_name.sql and index.txt)
                if null index.txt is shown to console to be used by sp__upgrade
    @opt        options
                lo      list only, fill #objs_list without store files
    @dbg        1=show execution info
                2=show objects to script without script and save to file
                3=more up ...

Examples
    sp__script_group_tofile ''''utility@s.zaglio'''',''''%temp%\utility''''
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_group_tofile'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_group_tofile:

-- ========================================================== sp__script_history
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_history',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140108
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_history') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_history') with nowait
        goto skip_sp__script_history
        end
    if @ver>140108
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_history') with nowait
        goto skip_sp__script_history
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_history') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_history'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_history]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:140108\s.zaglio: svr_id->srv_id
    r:120521\s.zaglio: added script header as -- or multi comment
    v:120517\s.zaglio: adapted to new fn__script_sysobjs
    v:120514\s.zaglio: added store of generic sql that begin with exec
    v:120509\s.zaglio: added store of generic sql
    r:120508\s.zaglio: adding store of generic sql
    r:120507\s.zaglio: added drop of objects and exists test on more crucial tables and index
    r:120504\s.zaglio: problem of drop fun X->create syn X
    r:120503\s.zaglio: introducing user in @obj to filter my objs
    v:120213\s.zaglio: adapted to new structures and fn__script_events
    r:120208\s.zaglio: adapting to new structures
    v:120206\s.zaglio: added usr column
    v:120126\s.zaglio: added top 100
    v:110830\s.zaglio: better help
    v:110624\s.zaglio: restyling of output and removed rev from scripting
    v:110622\s.zaglio: restyling of output
    r:110621\s.zaglio: fine filter on alter for non table objects
    r:110603\s.zaglio: added exclude
    r:110511\s.zaglio: added >= date
    r:110510\s.zaglio: generate script for historicized objects
    t:sp__Script_history ''-60\\wsj\tempsa'',@dbg=2
*/
CREATE proc sp__script_history
    @what       nvarchar(max) = null,
    @list       sysname = null,
    @grp        sysname = null,
    @opt        sysname = null,
    @dbg        int     = null
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
declare @top int select @top=100
if @what is null or object_id(''log_ddl'') is null goto help

-- ============================================================== declaration ==
declare
    @sql nvarchar(4000),@cond nvarchar(32),
    @id int,@dt datetime,@evot sysname,
    @tmp sysname,@sdt sysname,@usr sysname,
    @obj sysname, @obj_id sysname,@ev sysname,
    @drop bit,                                  -- option
    @i int,                                     -- index
    @iwhat sysname,                             -- inside truncated @what
    @crlf nvarchar(2),
    @lbl sysname

declare @excludes table(obj sysname)

-- insert before here --  @end_declare bit
if object_id(''tempdb..#src'') is null
    create table #src(lno int identity,line nvarchar(4000))

create table #objs(
    srv_id int,
    [db_id] int,
    id int,
    [name] sysname,
    [rel] sysname,
    [dt] datetime,
    ev sysname,
    evot sysname,       -- event object type
    [type] nvarchar(3) null,
    usr sysname null,
    uinfo sysname null,
    next_ev sysname null,
    next_evot sysname null
    )
alter table #objs add [sql] nvarchar(max)

-- =========================================================== initialization ==
select
    @drop=1-charindex(''|nodrop|'',@opt),
    @usr='''',
    @crlf=crlf
from fn__sym()

while (left(ltrim(@what),len(@crlf))=@crlf)
    select @what=substring(rtrim(ltrim(@what)),len(@crlf)+1,len(@what))

select @iwhat=left(ltrim(@what),128)

-- ======================================================== second params chk ==
if left(@iwhat,1)=''-''
    begin
    select @usr=''\\''+dbo.fn__str_at(@iwhat,''\\'',2)
    select @i=cast(dbo.fn__str_at(@iwhat,''\\'',1) as int)
    select @dt=getdate()+@i
    select @iwhat=convert(sysname,@dt,126)+@usr
    end

-- ===================================================================== body ==

if left(@iwhat,7)=''insert ''
or left(@iwhat,3)=''if ''
or left(@iwhat,5)=''exec ''
or left(@iwhat,3)=''-- ''
or left(@iwhat,3)=''/* ''
    begin
    -- select * from fn__script_sysobjs((select ev from tids))
    exec sp__script_store
            @et=''insert_data'',@obj=''data_script'',
            @sql=@what,@dbg=@dbg
    goto ret
    end

insert @excludes(obj)
select token
from dbo.fn__str_table(replace(replace(@list,'','',''|''),'';'',''|''),''|'')

insert #src(line) select ''/''+replicate(''*'',79)

if patindex(''____-__-__T__:__:__.___'',replace(@iwhat,'' '',''T''))>0
or patindex(''____-__-__T__:__:__.___\\%'',replace(@iwhat,'' '',''T''))>0
    begin
    if object_id(''fn__script_trace'') is null goto err_trc
    select
        @sdt=replace(dbo.fn__str_at(@iwhat,''\\'',1),'' '',''T''),
        @usr=''\\''+dbo.fn__str_at(@iwhat,''\\'',2)
    if @usr='''' select @usr=''%''
    insert #src(line) select ''** unScript from ''+@iwhat
    -- sp__script_history ''2012-02-08 14:37:14.057''
    -- drop table #objs
    -- select * from fn__script_trace(default,default,default) -- drop table #test
    -- declare @sql nvarchar(max),@iwhat sysname select @iwhat=''2011-04-15T16:00:33.920''

    -- truncate table #objs
    -- declare @sql nvarchar(4000),@iwhat sysname select @iwhat=''2011-06-20T18:04:57.800''
    -- declare @iwhat sysname select @iwhat=''2012-02-08T14:37:14.057''
    truncate table #objs

    -- this encapsulation into exec allow update without dependency from fn__script_trace
    insert #objs(srv_id,db_id,usr,id,[name],[sql],rel,dt,ev,evot,[type])
    select
        a.srv_id,a.db_id,a.usr,a.id,a.[obj],a.[code],
        case
        when a.rel is null
        then convert(sysname,a.dt,12)+''.''+
             replace(left(convert(sysname,a.dt,8),5),'':'','''')
        else convert(sysname,a.rel)
        end
        as rel,
        a.dt,
        upper(left(ev.cod,charindex(''_'',ev.cod)-1)) event,
        upper(substring(ev.cod,charindex(''_'',ev.cod)+1,128)) ev_obj_type,
        o.type
    -- select top 10 *
    from fn__script_trace(default,default,default) a
    -- obj must locally exists to ensure parent correct relations
    -- (drop of table do not involve trigger, index etc)
    left join sys.objects o with (nolock)
        on o.[name]=a.[obj]
    left join sys.indexes i with (nolock)
        on i.[name]=a.[obj]
    left join fn__script_sysobjs((select obj from tids)) so
        on so.cod=object_name(i.object_id)
    join fn__script_sysobjs((select ev from tids)) ev
        on a.ev=ev.id
    where dt>=@sdt
    and a.flags in (''ver'')
    -- obj. and parent must exists (but include script for data)
    and not (coalesce(o.object_id,i.object_id) is null
             and a.[obj]!=''DATA_SCRIPT''
            )
    -- exclude generated objs
    and not (charindex(''generated by sp__script_alias'',a.[code])
                between 20 and 80
             or
             charindex(''g:sp__script_alias'',a.[code])
                between 3 and 20
            )
    -- and a.udt is null
    and a.usr like @usr
    and so.id is null       -- and index do not refer to system obj
    order by dt,id

    -- exclude forwarded objs
    update o set
        uinfo=left(cast(oinfo.val2 as nvarchar(4000)),128)
    from #objs o
    cross apply fn__script_info(o.[name],''v'',''0'') oinfo
    end -- date
else
    goto help

insert #src(line) select ''** - objects in order of:''
insert #src(line) select ''**    tables,indexes,functions,views,procs,synonym,triggers''
insert #src(line) select ''** - dropped objects are not scripted''
insert #src(line) select ''** - all the alteration of tables, idxs, etc.''
insert #src(line) select ''** - only last of many alterations of fn & sp''
insert #src(line) select replicate(''*'',79)+''/''

if @dbg>1
    select *
    from #objs o
    order by dt desc,id desc

-- sp__script_history ''2011-04-15 19:13:02.520'' -- sp__script ''2267''
-- select not dropped objects
-- drop table #obj_to_script
-- declare @excludes table(obj sysname)
select
    identity(int,1,1) as row,o.*,
    -- 110622\s.zaglio: round up to 10 minutes
    substring(dbo.fn__format(o.dt,''YYYYMMDD_HHMM'',default),3,10)+''0''+usr.usr as usdt,
    uinfo
into #obj_to_script
-- select *
from (
    select
        [name],ev,evot,
        max(rel) as rel, max(dt) as dt,
        max(id) id
        -- case when count(*)>1 then max(id) else null end as id
    from #objs
    where ev!=''DROP''
    group by [name],ev,evot
    -- ,case evot when ''table'' then dt else 0 end -- causes duplicates
    ) o
left join (
    select
        [name],ev,evot,
        max(rel) as rel, max(dt) as dt,
        max(id) id
        -- case when count(*)>1 then max(id) else null end as id
    from #objs
    where ev=''DROP''
    group by [name],ev,evot
    ) d -- ropped
on d.[name]=o.[name] and d.evot=o.evot /*and d.ev!=o.ev*/ and d.dt>=o.dt
join #objs usr on o.id=usr.id and (@usr='''' or usr.usr=@usr)
left join @excludes ex on o.[name] like ex.[obj]
where d.ev is null
and   ex.[obj] is null
and   (not uinfo like ''generated by sp__script_%''
       or uinfo is null
      )
order by
    case o.evot
    when ''table'' then 10
    when ''index'' then 20
    when ''function'' then 30
    when ''view'' then 40
    when ''procedure'' then 50
    when ''synonym'' then 60
    when ''trigger'' then 70      -- can use some other func/proc/view
    else 99
    end
    ,dt,id

drop table #objs

-- show list of filtered objects to allow visual exclusion
select name,ev,evot,rel,dt,uinfo from #obj_to_script

-- declare @id int,@usr sysname,@dt datetime,@evot sysname
-- truncate table #src
insert #src(line) select ''''
select @tmp=''''
declare cs cursor local for
    select name,ev,id,dt,usdt,evot
    -- select *
    from #obj_to_script
    order by row
open cs
while 1=1
    begin
    fetch next from cs into @obj,@ev,@id,@dt,@usr,@evot
    if @@fetch_status!=0 break
    if @usr!=@tmp
        begin
        insert #src(line) select ''''
        insert #src(line)
            select ''/*''+dbo.fn__format(@usr,''*< '',75)+'' */''
        end

    select @obj_id=dbo.fn__hex(@id),
           @lbl=''skip_''+lower(dbo.fn__format(@obj,''AN'',default))

    if @drop=1
        begin

        -- try drop existing object if is not an alter table
        if @evot in (''procedure'',''function'',''synonym'',''view'',''trigger'')
            exec sp__script @obj,@opt=''drop''
        else
            begin
            if @evot =''table'' and @ev=''create''
                begin
                insert #src(line)
                select ''if exists(select null from sys.objects where name=''''''+@obj+'''''') ''
                insert #src(line) select ''goto ''+@lbl
                end
            if @evot =''index'' and @ev=''create''
                begin
                insert #src(line)
                select ''if exists(select null from sys.indexes where name=''''''+@obj+'''''') ''
                insert #src(line) select ''goto ''+@lbl
                end
            exec sp__script @obj_id,@opt=''drop''
            end
        if @lbl!=''skip_data_script''
            -- because all data_script has the same label
            insert #src select @lbl+'':''
        end
    else
        begin
        if @evot in (''procedure'',''function'') insert #src(line) select ''go''
        exec sp__script @obj_id
        insert #src(line) select ''go''
        end
    select @tmp=@usr
    end -- while of cursor
close cs
deallocate cs

insert #src(line) select ''''
insert #src(line) select ''goto ret''
insert #src(line) select ''''
insert #src(line) select ''err_drp:''
insert #src(line) select ''  raiserror(''''utilities must be installed before this operation'''',11,1)''
insert #src(line) select ''  goto ret''
insert #src(line) select ''ret:''

exec sp__print_table ''#src''

drop table #src

goto ret

-- =================================================================== errors ==
err_trc:    exec @ret=sp__err ''fn__script_trace is required'',@proc
            goto ret
err_log:    exec @ret=sp__err ''logddl not initialized'',@proc
            goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    generate script for historicized objects.

Notes
    func,proc,synonym,

Parameters
    @what       can be:
                * name of a single object: will list last 1000 modifications
                * id of trace where to begin: will script it
                * ISO8601 date of trace where to begin
                  (aaaa-mm-ggThh:mm:ss.mmm) or (aaaa-mm-gg hh:mm:ss.mmm)
                  will script all objects from that date
                * ISO8601 date\\pc\usr will script from that date of objects
                  modified by "\\pc\usr"
                * a data script that begin with "IF " or "INSERT " or "exec "

    @list       list objects separated by |,;
                if @iwhat is a script instruction, the @list excludes objects
                if @iwhat is a group name, the @list define and re-define membership
                (sys objects are automatically excluded)
    @grp        (TODO)group name for objects selected between @iwhat and @excludes
    @opt        option          description
                nodrop          do not script as code using sp_executesql

Fields list mean
    tid     type of object
    id      row table id
    srv_id  server id
    db_id   database id
    lid     local object id
    flags   ver=version or 0(Zero)
    obj     object name or hash
    des     sql command
    usr_id  user id
    app_id  application id
    rel     release captured from header
    dt      utc datetime
    udt     sync datetime

Examples
    sp__script_history "2011-05-06 17:37:31.837"

    -- script all objs changed in the last 120 days by user X from pc Y
    sp__script_history "-120\\Y\X"

    -- insert data script
    sp__script_history  ''''
                        if not exists(select null from tbl where id=123)
                        insert tbl(id,cd,txt)
                        select 123,''''''''cd'''''''',''''''''txt''''''''
                        ''''
''
select @ret=-1

if object_id(''log_ddl'') is null
    begin
    exec sp__printframe ''The log_ddl table is absent.'',
                        ''Use sp__script_trace_db to install the tracer''
    goto ret
    end

help_trace_view:
select @iwhat=upper(@iwhat)

if not object_id(''fn__script_trace'') is null
    begin
    select @sql=''
    select top (%top%)
        convert(binary(4),a.id) as id,
        convert(binary(4),a.srv_id) as srv_id,
        convert(binary(4),a.db_id) as db_id,
        a.obj,
        a.flags,
        a.usr,
        a.code,
        a.rel,
        upper(left(ev.cod,charindex(''''_'''',ev.cod)-1)) event,
        upper(substring(ev.cod,charindex(''''_'''',ev.cod)+1,128)) ev_obj_type,
        upper(left(ev.cod,charindex(''''_'''',ev.cod)-1)) event,
        a.dt
    from fn__script_trace(%what%,default,default) a
    join fn__script_sysobjs((select ev from tids)) ev
        on a.ev=ev.id
    where %cond%
    order by dt desc,id desc
    ''
    if @iwhat is null select @iwhat=''default'',@cond=''1=1''
    else
        if left(@iwhat,2)=''0x''
            select @cond=''id=''+@iwhat,@iwhat=''default''
        else
            select @iwhat=dbo.fn__str_quote(@iwhat,''''''''),@cond=''1=1''

    exec sp__str_replace @sql out,''%top%|%what%|%cond%'',@top,@iwhat,@cond
    if @dbg>0 exec sp__printsql @sql
    exec(@sql)
    if @@error!=0 and @dbg=0 exec sp__printsql @sql
    end
else
    exec sp__printf ''\n*** fn__script_trace absent; will be installed from this sp ***''

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_history'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_history:

-- =================================================== sp__script_info_tags_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_info_tags_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140117
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_info_tags_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_info_tags_test') with nowait
        goto skip_sp__script_info_tags_test
        end
    if @ver>140117
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_info_tags_test') with nowait
        goto skip_sp__script_info_tags_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_info_tags_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_info_tags_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_info_tags_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:extract,tags,test
    v:140117\s.zaglio: added special cases (as notes, see below)
    v:140114\s.zaglio: adapted to new info_Tags
    v:130926\s.zaglio: added dbg and test of parse of name
    v:130925\s.zaglio: test fn for all objects definitions
    v:130719\s.zaglio: test for fn__Script_info_tags
    r:130718\s.zaglio: test for fn__Script_info_tags
    o:130519\s.zaglio:sp__script_info_tags_test_old
    d:130519\s.zaglio:sp__script_info_tags_test_old
    t:sp__script_info_tags_test @dbg=1
*/
CREATE proc sp__script_info_tags_test
    @obj sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare @sql nvarchar(max),@grps sysname,@row int
-- =========================================================== initialization ==
select @obj=isnull(nullif(@obj,''''),''%'')
-- ======================================================== second params chk ==
-- =============================================================== #tbls init ==
create table #results(
    id int,
    obj sysname,
    xt sysname,
    sts varchar(2),
    err nvarchar(4000)
    )

-- ===================================================================== body ==
insert #results(id,obj,xt)
select object_id,[name],[type]
from sys.objects o
where [type] in (''P'',''V'',''TF'',''FN'',''IF'',''FI'',''TR'')
and name like @obj
union
-- special cases for db triggers
select object_id,[name],''TD''
from sys.triggers o
where parent_id=0 -- means db trigger

declare cs cursor local for
    select r.obj,definition
    from #results r
    join sys.sql_modules m on m.object_id=r.id
open cs
while 1=1
    begin
    fetch next from cs into @obj,@sql
    if @@fetch_status!=0 break

    if @dbg>0 exec sp__printf ''-- processing "%s"'',@obj

    begin try
    select top 0 * into #t from fn__script_info_tags(@sql,@grps,@row)
    drop table #t
    update #results set sts=''ok'' where obj=@obj
    end try
    begin catch
    update #results set sts=''ko'',err=error_message() where obj=@obj
    end catch
    end -- cursor cs
close cs
deallocate cs

if @dbg>0 exec sp__printf ''-- test parse of name''

declare cs cursor local for
    select r.obj,definition
    from #results r
    join sys.sql_modules m on m.object_id=r.id
open cs
while 1=1
    begin
    fetch next from cs into @obj,@sql
    if @@fetch_status!=0 break
    if @dbg>0 exec sp__printf ''-- processing "%s"'',@obj

    begin try
    declare @parsed sysname
    select @parsed=parsename(val3,1),@row=row
    from fn__script_info(@obj,''#'',default) f
    where f.obj=@obj

    if @parsed!=@obj or @row<1
        update #results set sts=''ko'',err=isnull(err+'';'','''')+''obj name wrong parse''
        where obj=@obj
    else
        update #results set sts=''ok'' where obj=@obj and left(sts,2)!=''ko''
    end try
    begin catch
    update #results set sts=''ko'',err=error_message() where obj=@obj
    end catch
    end -- cursor cs
close cs
deallocate cs

if @dbg>0 exec sp__printf ''-- test artefact cases''
if 1=(select count(*) from (
    select *
    from fn__script_info_tags(''
                /*  leave this
                    l:
                    g:web
                    v:130515\s.zaglio:
                        /*test1*/
                    v:130129\s.zaglio: recreate procedure
                */
                create view vi
                as
                select 1 as one,2 two,3 three
                /* last but not least create */
                ...
                /* wrong comment or something inside a string
                '',''#'',default) rst
    union (
        select
            ''#'' as tag,
            257 as row,
            ''create'' as val1,
            ''view'' as val2,
            ''[vi]'' as val3,
            null as sts
        )
    ) cnt)
    insert #results(sts,obj,xt) select ''ok'',''_artefact case'',''''
else
    insert #results(sts,obj,xt) select ''ko'',''_artefact case'',''''

goto skip_cases

-- ============================================================ special cases ==
-- 140117\s.zaglio

select *
from fn__script_info_tags(''
 /*  leave this
    l:
    g:web
    v:131212\author.1: comment
    v:131204\author.2: comment

    Non properly conformant comment

        print @@version
        print @@servername

*/
-- v1.0.090721/fname l. -- old style comment
-- v1.0.090603/fname l. -- old style comment
--                              another generic comment
CREATE PROCEDURE SP_SCRIPT_INFO_TAGS_TEST1
as
print 10
'',default,default)

select *
from fn__script_info_tags(''
/*  leave this
    l:see LICENSE file
    g:utility
    v:091216\s.zaglio:added @@servername
    v:091018\s.zaglio:
    Statistic of SQL-Server - System parameters for dynamic collection.

    @@cpu_busy/1000 - Returns the time in seconds that the CPU has spent working since SQL Server was last started.
    @@io_busy/1000  - Returns the time in seconds that SQL Server has spent performing
    */
    /*
    Daily Version.

    Output - Table TBL_SERVERSTATISTICS. A Table include Row per run a Procedure,except Saturday.
    -- select * from TBL_SERVERSTATISTICS
    -- select * from TBL_SERVERSTATISTICS_PRIOR
*/
CREATE PROCEDURE [dbo].[sp__perf_daily]  ( @BIT_DELETE_RESULTS BIT = 0 )
AS
print 10
'',default,default)

skip_cases:

-- =========================================================== output results ==

exec sp__select_astext ''
    select sts,obj,xt,err from #results order by sts
    ''
exec sp__printf ''''
exec sp__prints ''8<''
exec sp__printf ''''

if exists(select top 1 null from #results where sts=''ko'')
    raiserror(''test failed'',16,1)

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.


-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test fn__script_info_tags for all objects definitions

Parameters
    [param]     [desc]
    @obj        filter for object(s) (default is %)
    @opt        (not used)
    @dbg        1 print object in process
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_info_tags_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_info_tags_test:

-- ============================================================= sp__script_jobs
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_jobs',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140109
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_jobs') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_jobs') with nowait
        goto skip_sp__script_jobs
        end
    if @ver>140109
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_jobs') with nowait
        goto skip_sp__script_jobs
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_jobs') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_jobs'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_jobs]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:140109\s.zaglio: problem of ending \ when scripted to file.
    v:090909\s.zaglio: from ms support
    -- single test
    exec sp__script_jobs
    exec sp__run_cmd ''type %temp%\%@@servername%_jobs.sql''
    exec sp__run_cmd ''del %temp%\%@@servername%_jobs.sql /q''

    -- out to temp table
    create table #src (lno int identity(10,10),line nvarchar(4000))
    exec sp__script_jobs @out=''#src''
    select * from #src
    drop table #src

    -- out to specific dir
    declare @out sysname exec sp__get_env @out out,''allusersprofile''
    exec sp__script_jobs @out=@out,@dbg=1
    exec sp__run_cmd ''dir "%allusersprofile%"''
    exec sp__run_cmd ''del "%allusersprofile%\%@@servername%_jobs.sql" /q''
    -- exec sp__run_cmd ''del c:\temp\*.* /q /s /f''

*/
CREATE proc [dbo].[sp__script_jobs]
    @server nvarchar(30)=null,  -- server name to run script on. by default, local server.
    @out sysname=null,          -- can be #src or must terminate with .sql or \.
    @dbg bit=0
as
begin
set nocount on
if @server is null select @server=@@servername
--sp_oa params
declare @cmd nvarchar(255) -- command to run
declare @osqlserver int -- oa return object
declare @hr int -- return code

--user params
declare @filename nvarchar(200) -- file name to script jobs out

--sql dmo constants
declare @scripttype nvarchar(50)
declare @script2type nvarchar(50)
set @scripttype = ''327''  -- send output to file, transact-sql, script permissions, test for existence, used quoted characters.
set @script2type = ''3074''  -- script jobs, alerts, and use codepage 1252.

--set the following properties for your server
declare @tmp sysname
exec sp__get_temp_dir @tmp out
set @server =  lower(@server)
if right(@out,1)=''\'' select @out=left(@out,len(@out)-1)
if not @out is null and right(@out,4)!=''.sql'' and @out!=''#src''
    select @filename=@out
else
    set @filename = @tmp
select @filename=@filename+''\''+@server+''_jobs.sql''

if @dbg=1 exec sp__printf ''out to:%s'',@filename

--create the sqldmo object
exec @hr = sp_oacreate ''sqldmo.sqlserver'', @osqlserver out

--set windows authentication
exec @hr = sp_oasetproperty @osqlserver, ''loginsecure'', true

--connect to the server
exec @hr = sp_oamethod @osqlserver,''connect'',null,@server

--script the job out to a ntext file
set @cmd = ''jobserver.jobs.script('' + @scripttype + '',"'' + @filename +''",'' + @script2type + '')''
create table #devnul (t ntext) insert into #devnul -- prevent output
exec @hr = sp_oamethod @osqlserver, @cmd
drop table #devnul

if @out=''#src''
    begin
    exec sp__file_read @filename,''#src'',@step=10
    select @cmd=''del "''+@filename+''" /q''
    exec sp__run_cmd @cmd,@nooutput=1
    end

--close the connection to sql server
--if object is not disconnected, the processes will be orphaned.
exec @hr = sp_oamethod @osqlserver, ''disconnect''

--destroy object created.
exec sp_oadestroy @osqlserver
end -- [sp__script_jobs]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_jobs:

-- ============================================================= sp__script_move
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_move',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130802.1800
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_move') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_move') with nowait
        goto skip_sp__script_move
        end
    if @ver>130802.1800
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_move') with nowait
        goto skip_sp__script_move
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_move') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_move'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_move]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:move,back,history,historicize
    v:130802.1800\s.zaglio: adapted to new sp__script_template
    v:121122\s.zaglio: log n moved instead of @top
    v:121107\s.zaglio: added nolock near count(*)
    v:121003\s.zaglio: used sp__flds_list to follow synonyms
    v:121002\s.zaglio: follow the synonyms of tables using sp__script_pkey
    v:120921\s.zaglio: better skip when single table
    v:120920\s.zaglio: abount log times of @top instead of @n
    v:120919\s.zaglio: due runtime errors, some correction to aliases
    v:120918\s.zaglio: done a compilable version without errors
    r:120917\s.zaglio: adopting sp_select_asform
    r:120913\s.zaglio: modifing template and correct sp__script_template
    r:120912\s.zaglio: modifing template
    d:120910\s.zaglio: sp__script_copy
    r:120910\s.zaglio: removed tag opt. and converting in multi tab.
    r:120903\s.zaglio: changing to multi table
    v:120828\s.zaglio: added tag option
    v:120827\s.zaglio: added log of times
    v:120820\s.zaglio: added @ms... vars
    v:120810\s.zaglio: done
    r:120809\s.zaglio: script copy or move data
    t:sp__script_move_test @dbg=1
*/
CREATE proc sp__script_move
    @tbls nvarchar(4000) = null,    -- sa|sb|sc
    @dsts nvarchar(4000) = null,    -- da|db|dc
    @joins nvarchar(max) = null,    -- sa.id=sb.rid|sb.id=sc.rid
    @where nvarchar(4000) = null,   -- dt>...
    @order sysname = null,
    @top sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare
    @proc sysname, @err int, @ret int, -- @ret: 0=OK -1=HELP, any=error id
    @e_msg sysname, @e_p1 sysname

select
    @dbg=0,
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

select
    -- @tbl=   dbo.fn__sql_normalize(@tbl,default),
    -- @dst=   dbo.fn__sql_normalize(@dst,default),
    @where= dbo.fn__sql_normalize(@where,default),
    @order= dbo.fn__sql_normalize(@order,''ord''),
    @top=   dbo.fn__sql_normalize(@top,''top''),
    @joins= case when ltrim(rtrim(@joins))=''|''
                 then ''''
                 else isnull(@joins,'''')
            end

if left(@top,1)=''#'' goto err_top

if dbo.fn__str_count(@tbls,''|'')!=dbo.fn__str_count(@tbls,''|'') goto err_dp1
if @joins!=''''
and dbo.fn__str_count(@tbls,''|'')!=dbo.fn__str_count(@joins,''|'') goto err_dp2

-- ============================================================== declaration ==
declare
    @i int,@j int,
    @tbl sysname,@dst sysname,
    @dst_db sysname,
    @dst_tbl sysname,
    @pkey nvarchar(4000),               -- list of pk fields
    @on_pkeys nvarchar(4000),           -- list of tbl.pkf1=tmp.pkf1 and ...
    @excludes sysname,
    @notrg bit, @copy bit, @back bit,   -- options
    @idx bit,                           -- options
    @flds nvarchar(4000),               -- fields
    @tbl_flds nvarchar(4000),           -- fields with tbl. prefix
    @lbl sysname,                       -- local label for goto
    @keys sysname,                      -- #tmp table name for keys of @tbl
    @src bit,                           -- 1=return results into #src, 0=print
    @ntop sysname,                      -- value of @top
    @log sysname,                       -- name of log table
    @join_on nvarchar(1024),            -- on condition
    @alias sysname,@join sysname,
    @tmp sysname,
    @mix sysname,                       -- inner sp
    @id int,
    @end_declare bit

create table #tpl(
    lno int identity primary key,
    line nvarchar(4000)
    ,procid int default(@@procid)
    )
create table #tpl_sec(
    lno int identity,
    section sysname,
    line nvarchar(4000)
    ,procid int default(@@procid)
    )
create table #tpl_cpl(tpl binary(20),section sysname,y1 int,y2 int)

if exists(select top 1 null from #tpl) goto err_tpl

create table #vars (id nvarchar(16),value sql_variant)

if object_id(''tempdb..#src'') is null
    begin
    create table #src(lno int identity primary key,line nvarchar(4000))
    select @src=0
    end
else
    select @src=1

create table #joins(
    id int identity,
    src sysname,
    alias sysname,
    dst sysname,
    [join] nvarchar(4000) null,
    join_on nvarchar(4000),
    [pkey] nvarchar(1024) null,
    keys nvarchar(1024) null,
    [on_pkeys] sysname null,
    [main] sysname null,
    [main_alias] sysname null,
    dst_flds nvarchar(4000) null,
    tbl_flds nvarchar(4000) null,
    lbl sysname null -- normalized rtbl for skip_
    )

-- =========================================================== initialization ==

select
    @mix=''#mix'',
    @back =charindex(''|back|'',@opt),
    @notrg=charindex(''|notrg|'',@opt),
    @copy =charindex(''|copy|'',@opt),
    @idx  =charindex(''|idx|'',@opt),
    @log  =isnull(dbo.fn__str_between(@opt,''log:'',''|'',default),
                  ''tmp_script_copy_log''
                 ),
    @excludes='''',
    @excludes=@excludes+case @copy  when 1  then ''|move'' else '''' end,
    @excludes=@excludes+case @notrg when 1  then ''|trgs'' else '''' end,
    @excludes=@excludes+case @dbg   when 0  then ''|dbg''  else '''' end,
    @excludes=@excludes+case @idx   when 0  then ''|idxs'' else '''' end,
    @ntop=dbo.fn__Str_between(@top,''('','')'',default),
    @top=replace(@top,@ntop,''@top'')

-- load local template
exec sp__Script_templates ''move''

-- insert before here --  @end_declare=1
-- ======================================================== second params chk ==

insert into #joins(alias,src,dst,join_on)
select
    alias=case charindex('':'',src.token)
          when 0 then src.token
          else left(src.token,charindex('':'',src.token)-1)
          end,
    src=case charindex('':'',src.token)
        when 0 then src.token
        else substring(src.token,charindex('':'',src.token)+1,128)
        end,
    dst.token,
    isnull(replace(replace(jj.token,''|'','' and ''),''"'',''''''''),'''')
from dbo.fn__str_split(@tbls,''|'') src
left join dbo.fn__str_split(@dsts,''|'') dst on src.pos=dst.pos
left join dbo.fn__str_split(@joins,''|'') jj on jj.pos=dst.pos

update #joins set lbl=dbo.fn__format(alias,''AN'',default)

-- extract table from join_on
declare cs cursor local for
    select id,src,alias,join_on from #joins order by id
open cs
while 1=1
    begin
    fetch next from cs into @id,@tbl,@alias,@join_on
    if @@fetch_status!=0 break

    -- 121002\s.zaglio: will follow synonyms
    exec sp__script_pkey @pkey out,@tbl,@sep='','',@opt=''flds''
    if @pkey is null goto err_pk
    exec sp__flds_list @flds out,@tbl,'',''
    -- exec sp__printf ''tbl:%s pkey:%s flds:%s'',@tbl,@pkey,@flds

    select
        @keys=''#keys_''+lower(dbo.fn__format(@tbl,''ANs'',default)),
        @pkey=dbo.fn__flds_quotename(@pkey,'',''),
        @on_pkeys=replace(dbo.fn__str_exp(@alias+''.%%=tmp.%%'',@pkey,'',''),
                          '','','' and ''
                         ),
        @tbl_flds=dbo.fn__str_exp(@alias+''.%%'',@flds,'',''),
        @join='',''

    -- extra tables/aliases from joins
    if @joins=''''
        select @join=@tbl,@join_on=@keys
    else
        begin
        select @i=charindex(''.'',@join_on)
        while @i>0
            begin
            select @j=@i-1
            while @j>0 and not substring(@join_on,@j,1) like ''[[ =,]'' select @j=@j-1
            select @alias=substring(@join_on,@j+1,@i-@j-1)
            select @tmp=src from #joins where @alias in (alias,src)
            if charindex('',''+@tmp+'','',@join)=0 and @tmp!=@tbl
                select @join=@join+@tmp+'',''
            select @i=charindex(''.'',@join_on,@i+1)
            end
        if @join = '',''
            select @join=@tbl
        else
            select @join=substring(@join,2,len(@join)-2)
        end

    update #joins set
        [join]=@join,
        join_on=@join_on,
        pkey=@pkey,
        [keys]=@keys,
        on_pkeys=@on_pkeys,
        -- template tokens do not support crlf
        -- dst_flds=dbo.fn__str_flow(@flds,'','',default),
        -- tbl_flds=dbo.fn__str_flow(@tbl_flds,'','',default),
        dst_flds=@flds,
        tbl_flds=@tbl_flds,
        main=src, main_alias=alias
    where id=@id

    end -- cursor cs
close cs
deallocate cs

-- select @proc sp,* from #joins

if exists(
    select null
    from #joins
    where object_id(src) is null or object_id(dst) is null
    )
    goto err_tbl

if @joins!=''''
and exists(
    select null
    from #joins
    where isnull(join_on,'''')=''''
    and id!=1
    )
    goto err_jn

if @tbls is null or @dsts is null or @where is null -- and @opt=''||''
    goto help

-- if joins are specified the 1st table is the main table
if @joins!=''''
    begin
    update #joins set [join]=keys,[join_on]=on_pkeys where id=1
    update j set
        j.keys=jj.keys,
        j.on_pkeys=jj.on_pkeys,
        j.main=jj.src,
        j.main_alias=jj.alias
    from #joins j
    join #joins jj on j.[join]=jj.src
    -- where j.id!=1
    end

-- select @proc sp,* from #joins


-- ===================================================================== body ==
-- common info
insert #vars select ''%top%'',@top
insert #vars select ''%ntop%'',@ntop
insert #vars select ''%where%'',@where
insert #vars select ''%orderby%'',@order
insert #vars select ''%log%'',@log
exec sp__select_asform ''#joins'',''id=1'',@opt=''#vars''

if not object_id(''tempdb..''+@mix) is null drop proc #mix

exec(''create proc ''+@mix+'' @section sysname,@excludes sysname=null,@dbg int=0
      as
      select @excludes=''''''+@excludes+''''''+isnull(''''|''''+@excludes,'''''''')
      exec sp__script_template
        @section,
        @opt=''''replace"'''',
        @excludes=@excludes
        ,@dbg=@dbg
    '')

-- manually expand %log_times% for problem with backward compatibility
exec sp__script_template ''%log_times_section%'',''%log_times%''
exec sp__script_template ''%log_commit_section%'',''%log_commit%''

exec @mix ''%header%'',@dbg=@dbg
if @joins!='''' exec @mix ''%begin%'',@dbg=@dbg

-- select @proc sp,* from #joins

declare cs cursor local for
    select
        id,src,dst,alias,join_on,[join],pkey,keys,on_pkeys
    from #joins
    -- from last because main table is used in join with details
    order by id desc
open cs
while 1=1
    begin
    fetch next from cs into
        @id,@tbl,@dst,@alias,@join_on,@join,
        @pkey,@keys,@on_pkeys
    if @@fetch_status!=0 break

    select @where=''id=''+cast(@id as sysname)
    exec sp__select_asform ''#joins'',@where,@opt=''#vars''

    if @joins=''''
        exec @mix ''%body_single_table%'',@dbg=@dbg
    else
        begin
        select @excludes=case @id when 1 then ''main'' else null end
        exec @mix ''%body_group%'',@excludes,@dbg=@dbg
        end
    end -- cursor cs
close cs
deallocate cs

if @joins!='''' exec @mix ''%end%'',@dbg=@dbg
exec @mix ''%footer%'',@dbg=@dbg
-- if @back=1 exec @mix ''%back%''
exec @mix ''%catch%'',@dbg=@dbg

drop proc #mix

if @src=0 exec sp__print_table ''#src''

goto ret

-- =================================================================== errors ==
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1                    goto ret
err_top:    select @e_msg=@top                                          goto err
err_pk:     select @e_msg=''table %s without primary key'',@e_p1=@tbl     goto err
err_tbl:    select @e_msg=''source table not found''                      goto err
err_jn:     select @e_msg=''some join are not specified''                 goto err
err_tpl:    select @e_msg=''a tpl already exists from parent proc''       goto err
err_dp1:    select @e_msg=''@dsts count is different from @tbls''         goto err
err_dp2:    select @e_msg=''@joins count is differente from @tbls''       goto err
goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    create a script to copy rows from tables to their copy;
    can move related data if @where contain joins conditions.
    can be used to historicize data

Parameters
    (*)         duty parameters
    #src        if present, store result here
    @tbls       (*)source tables names separated by "|";
                aliases must prefixed with ":" (ex: "A1:ARCHIVE1")
    @dsts       (*)destination table (must in the same order of sources)
    @joins      when more than one table is involved, can/must be joined
                using these expressions, separated by "|"
    @where      (*)condition that split tables into data to move and
                data to keep back from history
    @order      optional order by condition
    @top        optional top rows (n) or percent (n%)
    @opt        options
                run     run the script immediatelly
                sel     return results as select
                copy    copy only, do not delete original rows
                notrg   do not disable triggers
                back    reverses @where and  move back data
                        from destination to original table
                idx     include disable and then rebuild of indexes
                nfo     print count of moved rows and store times into log table:
                        create table %log%(
                            dt datetime,
                            tbl sysname,
                            n int,
                            ms_key int,
                            ms_alt int,
                            ms_ins int,
                            ms_del int,
                            ms_commit int
                            )
                log:tbl store log info into table tbl instead of
                        default tmp_script_copy_log

Notes
    A check of number of lines moved, to prevent move of all data, will be done.

See
    sp__script_align

Examples
    -- move single table
    sp__script_move @tbls = "tbl",
                    @dsts = "history..tbl",
                    @where = "not dt<getdate()-30",
                    @order = "dt",
                    @top = 4000,
                    @opt = "back|move|nfo"
                    ,@dbg=1

    -- move multiple table
    sp__script_move @tbls = "T1:tbl1|T2:tbl2|T3:tbl3",
                    @dsts = "H1|H2|H3",             -- this are synonyms
                    @joins= "|T2.rid=T1.id|T3.rid=T2.id",
                    @where = "not dt<getdate()-30",
                    @order = "dt",
                    @top = 4000,
                    @opt = "back|move|nfo"
                    ,@dbg=1
    /*  results is
        T1.pkeys -> #k
        move T1 from T1+#k
        move T2 from T1+#k+T2
        move T3 from T1+#k+T2+T3                    -- note this
    */

    sp__script_move @tbls = "T1:tbl1|T2:tbl2|T3:tbl3",
                    @dsts = "H1|H2|H3",           -- this are synonyms
                    @joins= "T1.id=T2.rid|T1.id=T3.rid",
                    @where = "not dt<getdate()-30",
                    @order = "dt",
                    @top = 4000,
                    @opt = "back|move|nfo"
                    ,@dbg=1
    /*  results is
        T1.pkeys -> #k
        move T1 from T1+#k
        move T2 from T1+#k+T2
        move T3 from T1+#k+T3                       -- note this
    */
''

select @ret=-1
-- ===================================================================== exit ==
ret:
return @ret

end -- sp__script_copy'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_move:

-- ======================================================== sp__script_move_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_move_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130802
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_move_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_move_test') with nowait
        goto skip_sp__script_move_test
        end
    if @ver>130802
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_move_test') with nowait
        goto skip_sp__script_move_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_move_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_move_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_move_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130802\s.zaglio: test for sp__Script_move
*/
CREATE proc sp__script_move_test
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
        |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
-- if  @run=0 goto help


-- =============================================================== #tbls init ==

-- ===================================================================== body ==

if not object_id(''test_move'') is null drop table test_move
if not object_id(''test_move_copy'') is null drop table test_move_copy
create table test_move(id int identity primary key,a int,b sysname)
insert test_move select 1,''one''
insert test_move select 2,''two''
select * into test_move_copy from test_move

exec sp__script_move
        ''t:test_move'',''test_move_copy'',
        @where=''1=1'',
        @opt=''back|move|nfo'',
        @dbg=@dbg

-- ================================================================== dispose ==
dispose:
drop table test_move
drop table test_move_copy

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test for sp__Script_move

Parameters
    [param]     [desc]
    @opt        options
    @dbg
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_move_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_move_test:

-- ============================================================== sp__script_ole
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_ole',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100328
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_ole') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_ole') with nowait
        goto skip_sp__script_ole
        end
    if @ver>100328
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_ole') with nowait
        goto skip_sp__script_ole
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_ole') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_ole'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_ole]

begin try
exec dbo.sp_executesql @statement = N'/*  Leave this unchanged doe MS compatibility
    l:see LICENSE file
    g:utility,script
    v:100328\s.zaglio: renamed to sp__script_ole
    v:090801.1900\S.Zaglio: vertion 2.5 of sp__script (rewrite of old sp__generate_script)
    todo: test on checks, indexes, tables with foreign keys
    t:sp__script ''dtproperties'',@print=0
    t:
        -- sp__script -- give us the help with #src structure
        create table #src (lno int identity(10,10),line nvarchar(4000))
        exec sp__script_ole ''sp__script_ole'',''#src''
        exec sp__script_ole ''#src''                  -- this print the source
        drop table #src
    h: SQLDMO on MSDN see http://msdn.microsoft.com/en-us/library/aa258911(SQL.80).aspx
    h: for specific help see http://msdn.microsoft.com/en-us/magazine/bb985852.aspx
    h: for generic help see http://msdn.microsoft.com/en-us/magazine/cc301940.aspx
*/
CREATE proc [dbo].[sp__script_ole]
    @obj sysname=null out,         -- object name and output table name
    @out nvarchar(512)=null out,   -- file name or table or variale
    @conn sysname=null,            -- svr or svr|db or svr|db|uid or svr|db|uid|pwd
    @oc int=null,
    @step smallint=10,
    @print bit=1,
    @dbg bit=0
as
begin
set nocount on
/* Compatibility notes with MSSQL2005, MSSQL2008
To allow this sp to work with 2005 & 2008, must install backward feature on SQL2K and update on SQL2K5
download: http://download.microsoft.com/download/f/7/4/f74cbdb1-87e2-4794-9186-e3ad6bd54b41/SQLServer2005_BC.msi
(can need reboot)
Probably need also to disable some surface protection from SQL2005 surface area configuration such as:
- query remote ad hoc (enable openrowset, opendatase)
- ole automation
- enable xp_cmdshell
Can be done by code
EXECUTE sp_configure ''show advanced options'', 1 RECONFIGURE WITH OVERRIDE
EXECUTE sp_configure
EXECUTE sp_configure ''xp_cmdshell'', ''1'' RECONFIGURE WITH OVERRIDE
EXECUTE sp_configure ''Ole Automation Procedures'', ''1'' RECONFIGURE WITH OVERRIDE
EXECUTE sp_configure ''SMO and DMO XPs'', ''1'' RECONFIGURE WITH OVERRIDE
-- MSSQL2008
-- EXECUTE sp_configure ''Ad Hoc Distributed Queries'',1 RECONFIGURE WITH OVERRIDE
EXECUTE sp_configure
EXECUTE sp_configure ''show advanced options'', 0 RECONFIGURE WITH OVERRIDE
*/
declare @r int set @r=0
declare @msg nvarchar(4000)
declare @src_name sysname select @src_name=''#src''
declare @dir nvarchar(256)
declare @file nvarchar(256)
declare @cmd sysname
declare @crlf nvarchar(2) set @crlf=char(13)+char(10)
declare @sql nvarchar(4000)
declare @n int,@i int, @k int, @drop_file bit
declare @line nvarchar(4000)
declare @out_var bit,@out_file bit,@out_tbl bit
declare @svr sysname,@db sysname,@uid sysname,@pwd sysname,@trusted bit
declare @own sysname
declare @ntype int
select @db=parsename(@obj,3)
select @own=parsename(@obj,2)
select @obj=parsename(@obj,1)
if @own is null set @own=''dbo''
select @own=dbo.fn__sql_unquotename(@own)
select @obj=dbo.fn__sql_unquotename(@obj)
/*
    select @obj=@own+''.''+@obj
    --> give: cmd:addobjectbyname|source:ODSOLE Extended Procedure|desc:
        Incompatibilità tra tipi.|svr:16711422|@transf:
*/

if @obj=@src_name goto print_src
if @out=@src_name select @print=null

-- return db_name() and server if nulls
if @conn is null and @db is null select @db=db_name()
exec sp__parse_conn @conn,@svr out,@db out,@uid out,@pwd out,@trusted out

/*
if not @obj is null and charindex(''.'',@obj)>0 begin
    set @own=dbo.fn__str_at(@obj,''.'',1)
    set @obj=dbo.fn__str_at(@obj,''.'',2)
end
*/

-- sqldmo_script_type vars
-- see: http://msdn.microsoft.com/en-us/library/aa225364(SQL.80).aspx
declare @sqldmoscript_default int , @sqldmoscript_drops int , @sqldmoscript_objectpermissions int
declare @sqldmoscript_primaryobject int , @sqldmoscript_clusteredindexes int , @sqldmoscript_triggers int
declare @sqldmoscript_databasepermissions int , @sqldmoscript_permissions int , @sqldmoscript_tofileonly int
declare @sqldmoscript_bindings int , @sqldmoscript_appendtofile int , @sqldmoscript_nodri int
declare @sqldmoscript_uddtstobasetype int , @sqldmoscript_includeifnotexists int , @sqldmoscript_nonclusteredindexes int
declare @sqldmoscript_indexes int , @sqldmoscript_aliases int , @sqldmoscript_nocommandterm int
declare @sqldmoscript_driindexes int , @sqldmoscript_includeheaders int , @sqldmoscript_ownerqualify int
declare @sqldmoscript_timestamptobinary int , @sqldmoscript_sorteddata int , @sqldmoscript_sorteddatareorg int
declare @sqldmoscript_transferdefault int , @sqldmoscript_dri_nonclustered int , @sqldmoscript_dri_clustered int
declare @sqldmoscript_dri_checks int , @sqldmoscript_dri_defaults int , @sqldmoscript_dri_uniquekeys int
declare @sqldmoscript_dri_foreignkeys int , @sqldmoscript_dri_primarykey int , @sqldmoscript_dri_allkeys int
declare @sqldmoscript_dri_allconstraints int , @sqldmoscript_dri_all int , @sqldmoscript_driwithnocheck int
declare @sqldmoscript_noidentity int , @sqldmoscript_usequotedidentifiers int

-- format output definitions
declare @sqldmoscript_4usequoted int -- only columns and without collate

-- sqldmo_script2_type vars
-- see: http://msdn.microsoft.com/en-us/library/aa225398(SQL.80).aspx
declare @sqldmoscript2_default int , @sqldmoscript2_ansipadding int , @sqldmoscript2_ansifile int
declare @sqldmoscript2_unicodefile int , @sqldmoscript2_nonstop int , @sqldmoscript2_nofg int
declare @sqldmoscript2_marktriggers int , @sqldmoscript2_onlyusertriggers int , @sqldmoscript2_encryptpwd int
declare @sqldmoscript2_separatexps int , @sqldmoscript2_extendedproperty int , @sqldmoscript2_extendedonly int
declare @sqldmoscript2_nocollation int
/*
    test:
    declare @tbl sysname, @new sysname
    select @tbl=''put here the table'',@new=@tbl+''_renamed''
    create table #src (lno int identity(10,10),line nvarchar(4000))
    exec sp__script_ole @tbl,''#src'',@oc=1
    exec sp__script_ole @tbl,''#src'',@oc=20
    exec sp__script_ole @tbl,''#src'',@oc=32
    update #src set line=replace(line,@tbl,@new)
    select coalesce(line,'''') as line from #src order by lno
    drop table #src
*/

-- sp__script options
declare
    @oc_tbl smallint,@oc_dri smallint,@oc_trg smallint,@oc_prp smallint,
    @oc_col smallint, @oc_fk smallint,
    @oc_end_declare bit
select
    @oc_tbl=1,
    @oc_dri=20,
    @oc_trg=32,
    @oc=coalesce(@oc,@oc_tbl),
    @oc_prp=128,
    @oc_col=512,
    @oc_fk=1024

-- sqldmo_script_type values
-- see: http://msdn.microsoft.com/en-us/library/aa225364(SQL.80).aspx
set @sqldmoscript_default = 4                       set @sqldmoscript_drops = 1
set @sqldmoscript_objectpermissions = 2             set @sqldmoscript_primaryobject = 4
set @sqldmoscript_clusteredindexes = 8              set @sqldmoscript_triggers = 16
set @sqldmoscript_databasepermissions = 32          set @sqldmoscript_permissions = 34
set @sqldmoscript_tofileonly = 64                   set @sqldmoscript_bindings = 128
set @sqldmoscript_appendtofile = 256                set @sqldmoscript_nodri = 512
set @sqldmoscript_uddtstobasetype = 1024            set @sqldmoscript_includeifnotexists = 4096
set @sqldmoscript_nonclusteredindexes = 8192        set @sqldmoscript_indexes = 73736
set @sqldmoscript_aliases = 16384                   set @sqldmoscript_nocommandterm = 32768
set @sqldmoscript_driindexes = 65536                set @sqldmoscript_includeheaders = 131072
set @sqldmoscript_ownerqualify = 262144             set @sqldmoscript_timestamptobinary = 524288
set @sqldmoscript_sorteddata = 1048576              set @sqldmoscript_sorteddatareorg = 2097152
set @sqldmoscript_transferdefault = 422143          set @sqldmoscript_dri_nonclustered = 4194304

set @sqldmoscript_dri_clustered = 8388608           set @sqldmoscript_dri_checks = 16777216
set @sqldmoscript_dri_defaults = 33554432           set @sqldmoscript_dri_uniquekeys = 67108864
set @sqldmoscript_dri_foreignkeys = 134217728       set @sqldmoscript_dri_primarykey = 268435456
set @sqldmoscript_dri_allkeys = 469762048           set @sqldmoscript_dri_allconstraints = 520093696
set @sqldmoscript_dri_all = 532676608               set @sqldmoscript_driwithnocheck = 536870912
set @sqldmoscript_noidentity = 1073741824           set @sqldmoscript_usequotedidentifiers = -1

-- format
set @sqldmoscript_4usequoted =-1

-- sqldmo_script2_type values
-- see: http://msdn.microsoft.com/en-us/library/aa225398(SQL.80).aspx
set @sqldmoscript2_default = 0                      set @sqldmoscript2_ansipadding = 1
set @sqldmoscript2_ansifile = 2                     set @sqldmoscript2_unicodefile = 4
set @sqldmoscript2_nonstop = 8                      set @sqldmoscript2_nofg = 16
set @sqldmoscript2_marktriggers = 32                set @sqldmoscript2_onlyusertriggers = 64
set @sqldmoscript2_encryptpwd = 128                 set @sqldmoscript2_separatexps = 256
set @sqldmoscript2_extendedproperty = 4194304       set @sqldmoscript2_extendedonly=67108864
set @sqldmoscript2_nocollation = 8388608

-- convertion from sp__Script options to sqldmo options
declare @type nchar(2),@options2 int ,@options int
select @options=case
    when @oc=@oc_prp then           @sqldmoscript_default   -- if only xprops
    when @oc&64=64 then               @sqldmoscript_default
                                    | @sqldmoscript_ownerqualify
                                    | @sqldmoscript_dri_defaults
    when @oc&@oc_tbl=@oc_tbl then   @sqldmoscript_default
                                    | @sqldmoscript_dri_defaults
                                    | @sqldmoscript_ownerqualify
    when @oc&@oc_fk=@oc_fk then     @sqldmoscript_dri_foreignkeys
                                    | @sqldmoscript_nodri
                                    | @sqldmoscript_driwithnocheck
--    sp__script ''%tbl%'',@oc=134
    when @oc&@oc_dri=@oc_dri then   @sqldmoscript_nodri
                                    | @sqldmoscript_dri_primarykey
                                    | @sqldmoscript_nonclusteredindexes
                                    | @sqldmoscript_dri_clustered
                                    | @sqldmoscript_indexes
                                    -- | @sqldmoscript_dri_defaults -- this don''t work
    when @oc&@oc_trg=@oc_trg then   @sqldmoscript_triggers
                                    | @sqldmoscript_nodri
                                    | 0
    else                            @sqldmoscript_default
                                    --| @sqldmoscript_nodri | @sqldmoscript_dri_defaults
                                    | @sqldmoscript_ownerqualify
                                    | @sqldmoscript_noidentity
    end

if (@options is null) select @options=@sqldmoscript_default

set @options2=  @sqldmoscript2_ansifile|@sqldmoscript2_unicodefile|
                case when @oc&@oc_prp=@oc_prp and @oc!=@oc_prp then @sqldmoscript2_extendedproperty
                when @oc=@oc_prp then @sqldmoscript2_extendedonly else 0 end|
                case when @oc&@oc_col=@oc_col then 0 else @sqldmoscript2_nocollation end
-- HELP

if (@obj=''-?'' or @obj is null) goto help

if left(@out,1)=''#'' and @out!=@src_name goto err_tmptbl
select @sql=quotename(@own)+''.''+quotename(@obj)
if @conn is null and dbo.fn__exists(@sql,null)=0 goto err_objnf

-- use as @tmp
select @msg=''tmp_''+replace(convert(nvarchar(64),newid()),''-'',''_'')+''.sql''

-- a temp destination file is required by OLE method call
if @out=@src_name --- we presume that exist and is compatible to (lno,line,...)
    begin
    exec sp__get_temp_dir @file out
    set @file=@file+@msg
    end
else
    begin
    create table #src (lno int identity(10,10),line nvarchar(4000))
    if @out is null select @out=@msg,@drop_file=1
    if charindex(''\'',@out)=0 -- out can be: file [1], dir\ [2] , dir\file [3]
        begin
        exec sp__get_temp_dir @file out -- [1]
        select @file=@file+''\''+@out
        end
    else
        begin
        if right(@out,1)=''\'' select @file=@out+@msg -- [2]
        end
    end -- !=#src

select @msg=null
select @sql=N''select @type=xtype from [''+@db+'']..sysobjects where [name]=''''''+@obj+''''''''
exec sp_executesql  @sql,N''@type nchar(2) out'',
                    @type=@type out

/*
if @type in (''V'',''P'',''TF'',''FN'',''IF'')
    begin
    todo: to refine better
    select @i=min(colid),@n=max(colid) from syscomments where id=object_id(@obj)
    while (@i<=@n)
        begin
        select @line=[ntext] from syscomments
        where id=object_id(@obj) and colid=@i
        select @i=@i+1
        insert into #src(line) select token from dbo.fn__str_table(@line,@crlf) order by pos
        end
    end -- source in syscomments
else
    begin
*/


declare @object_types nvarchar(50)
-- used to translate sysobjects.type into the bitmap that transfer requires
set @object_types=''t     v  u  p     d  r  tr          fn tf if ''
set @type=case @type when ''tf'' then ''fn'' when ''if'' then ''fn'' else @type end
set @ntype=power(2,(charindex(@type+'' '',@object_types)/3))

declare @hr int

declare @server int, @transf int    -- handle objects

select @cmd=''SQLDMO.SQLServer''
exec @hr=sp_oacreate @cmd, @server out if @hr<>0 goto err_dmo
select @cmd=''SQLDMO.Transfer''
exec @hr=sp_OaCreate @cmd, @transf out if @hr<>0 goto err_dmo

if @trusted=1 begin
    if @dbg=1 print ''secure login''
    select @cmd=''loginsecure''
    exec @hr = sp_oasetproperty @server, @cmd, 1 if (@hr <> 0) goto err_dmo
    select @cmd=''connect''
    exec @hr = sp_oamethod @server, @cmd, null, @svr if (@hr <> 0) goto err_dmo
    end
else begin
    if @dbg=1 print ''connection''
    select @cmd=''connect''
    if coalesce(@pwd,'''')='''' exec @hr = sp_oamethod @server, @cmd, null, @svr, @uid if (@hr <> 0) goto err_dmo
    else exec @hr = sp_oamethod @server, @cmd, null, @svr, @uid, @pwd if (@hr <> 0) goto err_dmo
end

select @cmd=''copydata''
exec @hr = sp_oasetproperty @transf, @cmd, 0                            if (@hr <> 0) goto err_dmo
select @cmd=''copyschema''
exec @hr = sp_oasetproperty @transf, @cmd, 1                          if (@hr <> 0) goto err_dmo
--if @type=''ut''
--exec @hr = sp_oasetproperty @transf, ''includedependencies'', 1                 if (@hr <> 0) goto err_dmo
select @cmd=''addobjectbyname''
exec @hr = sp_oamethod @transf, @cmd, null, @obj, @ntype, @own   if (@hr <> 0) goto err_dmo
select @cmd=''scripttype''
exec @hr = sp_oasetproperty @transf, @cmd, @options                   if (@hr <> 0) goto err_dmo
if @dbg=1 exec sp__printf ''options=%d'',@options
select @cmd=''script2type''
exec @hr = sp_oasetproperty @transf, @cmd, @options2                 if (@hr <> 0) goto err_dmo
if @dbg=1 exec sp__printf ''options2=%d'',@options2


set  @cmd =   ''Databases("'' + @db + ''").ScriptTransfer''
if @dbg=1 exec sp__printf ''-- %s'',@cmd
create table #devnul (t ntext) insert into #devnul -- prevent output
exec @hr=sp_oamethod @server, @cmd,null, @transf, 2, @file if (@hr <> 0) goto err_dmo
drop table #devnul

/*
-- sometimes cause an unk err and not accept the databases cmd
declare @object int
select @cmd=''databases''
exec @hr = sp_oagetproperty @server, @cmd, @object out                 if (@hr <> 0) goto err_dmo
select @cmd=''item''
exec @hr = sp_oamethod @object, @cmd, @object out, @db                      if (@hr <> 0) goto err_dmo
select @cmd=''scripttransfer''
create table #devnul (t ntext) insert into #devnul
exec @hr = sp_oamethod @object, @cmd,null, @transf, 2, @file      if (@hr <> 0) goto err_dmo
drop table #devnul
*/

exec sp_oadestroy @transf set @transf=null
select @cmd=''disconnect''
exec @hr=sp_oamethod @server, @cmd if (@hr <> 0) goto err_dmo
exec sp_oadestroy @server set @server=null

exec sp__file_read @file,@src_name,@step=@step,@dbg=@dbg
-- correct a problem that the editor introduce with a double #13 or #10
select @sql=''update ''+@src_name+'' set line=replace(replace(line,char(13),'''' ''''),char(10),'''' '''')''
exec(@sql)
set @obj=@src_name

--end -- use of sqldmo

print_src:
exec sp__count @src_name,@n=@n out
if @n=0 exec sp__printf ''-- no source rows found''
if @dbg=1 exec sp__printf ''%d rows in source'',@n

if @n>0 and @print=1
    begin
    if @dbg=1 exec sp__printf ''printing...''
    exec sp__print_table ''#src''
    end -- print

if @print=0
    begin
    if @dbg=1 set @sql=''select * from ''+@obj+'' order by lno''
    else set @sql=''select line from ''+@obj+'' order by lno''
    exec (@sql)
    drop table #src
    end

-- c: see http://www.simple-talk.com/sql/t-sql-programming/the-tsql-of-text-files/

if @drop_file=1 begin
    set @sql=''del /q "''+@file+''"''
    if @dbg=1 print @sql
    exec xp_cmdshell @sql,no_output
end

goto ret

help:

select @msg =''\ntmp decl: create table #src (lno int identity(''+convert(sysname,@step)+'',''+convert(sysname,@step)+''),line nvarchar(4000))\n\n''
            +''@options can be (NB: cannot be combined):\n''
            +''\t@oc_tbl=1   -- table with owner and not chk\n''
            +''\t@oc_dri=20  -- pkey,idx and chk\n''
            +''\t@oc_trg=32  -- triggers only\n''
            +''\t@oc_prp=128 -- with extended properties or only extended prop. if alone\n''
            +''\t@oc_col=512 -- add collation definition\n''
            +''\t@oc_fk=1024 -- only foreign key(on obj table)\n''
            +''\tdefault=@oc_tbl\n''
            +''\tfor foreign keys source use "sp__script_fkeys @table"\n''
            +''\n''
            +''Examples\n''
            +''\tsp__script_ole ''''sp__script'''' -- print source\n''
            +''\tsp__script_ole ''''sp__script'''',@print=0 -- output a select\n''
            +''\tsp__script_ole ''''sp__script'''',@out=''''#src'''',@print=null -- append to standard temp #src table\n''
            +''\tsp__script_ole ''''sp__script'''',@out=''''sp__script.sql'''',@print=null -- output to %temp%\sp__script.sql\n''
            +''\n''

exec sp__usage ''sp__script_ole'',@extra=@msg
select @msg=null
goto ret

err_tmptbl:select @msg=''-- temp table name must be #src'',@r=1 goto ret
err_objnf:select @msg=''-- object "''+@obj+''" not found'',@r=1 goto ret
err_dmo:
ret:
if not (@server is null and @transf is null)
    begin
    DECLARE @source nvarchar(255)
    DECLARE @description nvarchar(255)

    EXEC @hr = sp_OAGetErrorInfo @transf, @source OUT, @description OUT
    exec sp__printf ''-- cmd:%s|source:%s|desc:%s|svr:%s|@transf:%d'',@cmd,@source,@description,@server,@transf

    destory:
    if not @server is null exec sp_oadestroy @server
    if not @transf is null exec sp_oadestroy @transf
    end

if not @msg is null exec sp__printf @msg

return @r
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_ole:

-- ============================================================ sp__script_parse
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_parse',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091128
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_parse') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_parse') with nowait
        goto skip_sp__script_parse
        end
    if @ver>091128
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_parse') with nowait
        goto skip_sp__script_parse
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_parse') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_parse'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_parse]

begin try
exec dbo.sp_executesql @statement = N'/*  Keep this due MS compatibility
    l:see LICENSE file
    g:utility
    r:091128\s.zaglio: added group and continued develop
    r:090921\s.zaglio: select source code of @obj withhtml
    t:sp__script_tohtml ''sp__script_tohtml''
*/
create proc [dbo].[sp__script_parse]
    @obj sysname=null,
    @dbg smallint=0                      -- enable print of debug info
as
begin
set nocount on
if @dbg>=@@nestlevel exec sp__printf ''level of debugging:%d'',@@nestlevel
-- declarations
declare
    @proc sysname,      -- for sp__trace
    @msg nvarchar(4000),-- generic messages
    @sql nvarchar(4000),-- for inner sql
    @ret int,           -- return ok if 0 error if <0 or a value if >0
    @err int,           -- user for pure sql statements
    @i int,@n int,      -- generix index variables
    @timer datetime,    -- used to trace times
    @token sysname,
    @tl sysname,
    @p int,
    @in_comment_line bit,
    @in_comment bit,
    @in_string bit,
    @line nvarchar(4000),@html nvarchar(4000),
    @step int,
    @end_declare bit    -- close the declaration section so can add
                        -- a new in the middle

-- specific declarations; is better keep all on top
declare @name sysname       -- generic name/string
declare @array table (id int identity,name sysname)  -- generic list

-- initialization
select  @proc=''sp__script_tohtml'',
        @ret=0

-- exec sp__elapsed @timer out,''** init of sp__style: ''

-- parameters check
if @obj is null goto help

/* ================================ body ================================== */
-- sp__script
create table #src (lno int identity(10,10),line nvarchar(4000))
exec sp__script @obj,@out=''#src''
select @in_comment=0,@in_comment_line=0,@in_string=0,@step=10
select @i=min(lno),@n=max(lno) from #src
exec sp__printf ''%s\n%s'',''<html>'',''<body>''
while (@i<=@n)
    begin
    select @line=line from #src where lno=@i
    select @i=@i+@step
    -- follows tockens
    if @line is null exec sp__printf ''<br>''
    else
        begin
        select @p=null,@tl=null,@html=''''
        while (@p is null or (@p!=0 and @p<len(@line)))
            begin
            select @token=null
            exec sp__token @line,@token out,@p out,@tl=@tl out,
                @in_comment_line=@in_comment_line out,
                @in_comment=@in_comment out,
                @in_string=@in_string out
            -- sp__Script ''fn__token_sql''
            if dbo.fn__token_sql(@token)=1
            -- and @in_comment=0 and @in_comment_line=0
            -- and @in_string=0
                select @html=@html+@tl+''<b>''+@token+''</b>''
            else
                select @html=@html+@tl+coalesce(@token ,'''')
            -- exec sp__printf ''[%s](%s)'',@tl,@token
            if @token is null break
            end -- token scans
        exec sp__printf ''%s<br>'',@html
        end -- if not blank line
    end -- lines
exec sp__printf ''%s\n%s'',''</body>'',''</html>''
drop table #src
goto ret -- end of body

/* =============================== errors ================================= */
err:        -- init of error management

/* ================================ help ================================== */
help:
exec sp__usage @proc
select @msg=null,@ret=-1    -- generic Help error

ret:     -- procedure end...is better than return
if not @msg is null
    begin
    exec sp__printf @msg
    -- exec sp__trace @msg,@last_id=@ret out,@proc=@proc
    end
return @ret
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_parse:

-- ============================================================= sp__script_pkey
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_pkey',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121003
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_pkey') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_pkey') with nowait
        goto skip_sp__script_pkey
        end
    if @ver>121003
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_pkey') with nowait
        goto skip_sp__script_pkey
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_pkey') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_pkey'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_pkey]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:121003\s.zaglio: use of script_synonym
    v:121002\s.zaglio: script pkey or return list of fields
    t:sp__script_pkey null,''cfg'',@opt=''flds'',@dbg=1
*/
CREATE proc sp__script_pkey
    @pkey nvarchar(4000) =null out,
    @obj sysname = null,
    @sep nvarchar(32) = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    -- @i int,-- @n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    @flds bit,@defs bit,
    @db sysname,
    @sql nvarchar(4000),
    @end_declare bit

-- =========================================================== initialization ==
select
    @pkey=null,
    @flds=charindex(''|flds|'',@opt),
    @defs=charindex(''|defs|'',@opt),
    @end_declare=1

-- ======================================================== second params chk ==
if isnull(@obj,'''')='''' goto help
if @defs=1 goto err_nos

if not parsename(@obj,4) is null goto err_svr

-- follow synonym
exec sp__script_synonym @obj out,@obj,@opt=''path''

if object_id(@obj,N''U'') is null goto ret

-- ===================================================================== body ==

select @db=isnull(parsename(@obj,3),db_name())

select @sql=''
select @pkey=isnull(@pkey+@sep,'''''''')+columnname
from [''+@db+'']..fn__script_idx(object_id(@obj,N''''U''''))
where [primary]=1
order by index_column_id
''
if @dbg=1 exec sp__printsql @sql
exec @ret=sp_executesql
            @sql,
            N''@pkey nvarchar(4000) out,@sep nvarchar(32),@obj sysname'',
            @pkey out,@sep,@obj
if @@error!=0 or @ret!=0 goto err_sql

if @flds=0 goto err_nos

if @dbg=1 exec sp__printf ''-- pkey=%s'',@pkey

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_nos:    select @e_msg=''only "flds" option is admitted''              goto err
err_sql:    select @e_msg=''into inside code''                            goto err
err_syn:    select @e_msg=''cannot follow more than 3 levels of synonyms''goto err
err_svr:    select @e_msg=''server not admitted in the name''             goto err
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    return list of fields of pk of @obj or alter code

Parameters
    @pkey   out of list or code
    @obj    is te name of obj; can be a synonym or contain db
    @sep    separator for fields (default is pipe |)
    @opt    options
            flds    list fields separated by @sep
            defs    list definitions (TODO)

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_pkey'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_pkey:

-- ============================================================= sp__script_prop
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_prop',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110628
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_prop') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_prop') with nowait
        goto skip_sp__script_prop
        end
    if @ver>110628
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_prop') with nowait
        goto skip_sp__script_prop
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_prop') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_prop'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_prop]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:110628\s.zaglio: added scripting of trigger db
    v:110406\s.zaglio: removed @out
    v:100919\s.zaglio: adapted to mssql2k
    v:100404\s.zaglio: script properties
    t:
        exec sp__comment ''[sp__script_prop]'',''test sp..scr..prp''
        exec sp__comment ''[sp__script_prop].[@obj]'',''test sp..scr..prp''
        exec sp__script_prop ''sp__script_prop''
*/
CREATE proc [dbo].[sp__script_prop]
    @obj sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0
if @obj is null goto help

if dbo.fn__isMSSQL2K()=1
    begin
    raiserror(''warning!! sp__Script_prop not compatible with mssql2k'',16,1)
    goto ret
    end

if @dbg=1 exec sp__printf ''-- sp__script_prop scope''

declare
    @step int,@type nvarchar(2),
    @db sysname,@sch sysname,@sch_id int,
    @obj_ex sysname,@obj_in sysname,
    @sql nvarchar(4000),
    @lno_begin int,@lno_end int,
    @id int

declare @src table(lno int identity primary key,line nvarchar(4000))

select
    @db =parsename(@obj,3),
    @sch=parsename(@obj,2),
    @obj=parsename(@obj,1)
if @db is null select @db=db_name()
select @sch=[name],@sch_id=id from dbo.fn__schema_of(@obj)
select @obj_ex=quotename(@db)+''.''+coalesce(quotename(@sch),'''')+''.''+quotename(@obj)

select @id=object_id(@obj_ex)
if @id is null and dbo.fn__isMSSQL2K()=0
    select @id=object_id from sys.triggers where [name]=@obj

if @id=0 goto err_nof


select @step=ident_incr(''tempdb..#src'')

insert @src(line)
select ''exec sp__comment ''''''+quotename(@obj)+coalesce(''.''+quotename(column_name),'''')+'''''',''+
                         ''''''''+replace(convert(nvarchar(4000),[value]),'''''''','''''''''''')+''''''''
from dbo.fn__comments(null) where obj_id=object_id(@obj_ex)

if object_id(''tempdb..#src'') is null
    select line from @src order by lno
else
    insert #src(line) select line from @src order by lno

goto ret

-- =================================================================== errors ==
err_nof:    exec @ret=sp__err ''object %s not found'',@proc,@p1=@obj_ex goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc
select @ret=-1

ret:
return @ret
end -- sp__script_prop'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_prop:

-- =========================================================== sp__script_reduce
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_reduce',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090801
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_reduce') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_reduce') with nowait
        goto skip_sp__script_reduce
        end
    if @ver>090801
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_reduce') with nowait
        goto skip_sp__script_reduce
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_reduce') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_reduce'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_reduce]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this due MS compatibility
    l:see LICENSE file
    g:utility
    v:090801\S.Zaglio:apply some code transformation
    t:
        create table #src (lno int identity(10,10),line nvarchar(4000))
        create table test_upzcode (id int, c nchar, v nvarchar(12), t ntext,t1[ntext],i int)
        exec sp__script ''test_upzcode'',@out=''#src''
        exec sp__script_reduce 12    -- 4=to unicode+8
        print ''-----------------------------------''
        exec sp__script ''#src''  -- print output
        print ''-----------------------------------''
        drop table test_upzcode
        exec sp__recompile ''#src'',@dbg=1
        drop table #src
        drop table test_upzcode
*/
CREATE proc [dbo].[sp__script_reduce]
    @normalize tinyint=null,
    @step int=10,
    @dbg bit=0
as
begin
set nocount on
declare
    @sql nvarchar(4000),@msg nvarchar(4000),@proc sysname,
    @line nvarchar(4000),@i int,@n int,@crlf nchar(2),@j int,@m int,
    @t datetime,@keywords sysname,@replacers sysname,
    @replaced sysname,@keyword sysname,@replacer sysname,@exp sysname

select @proc=''SP__SCRIPT_REDUCE''
if @normalize is null goto help

-- @normalize is used as integer but defined as bit for the future specialization

select @crlf=char(13)+char(10)

-- delete white lines
if (@normalize & 1)=1
    delete from #src where line is null

-- remove collates and constrains
if (@normalize & 2)=2
    update #src set line=dbo.fn__RegexReplace(''\s(collate|constraint)\s[^\s]*'','''',line,1,1)

-- upsize nvarchar to nvarchar ad ntext to ntext and nchar to nchar
if (@normalize & 4)=4
    begin
    set @t=null
    /* todo:
    create function fn__word_find(@sentence nvarchar(4000),@word sysname,@lt sysname,@rt sysname) returns int
    create function fn__word_replace(@sentence nvarchar(4000),@word sysname,@replace sysname,@lt sysname,@rt sysname) returns nvarchar(4000)
    */

    select @keywords=''varchar|char|text''
    select @replacers   =''(%tkn%,|''
                        +''(%tkn%)|''
                        +'' %tkn% |''
                        +'' %tkn%(|''
                        +''[%tkn%]|''
                        +''(%tkn%(|''
                        +char(9)+''%tkn%(|''
                        +'' %tkn%,|''
                        +'' %tkn%)|''
                        +'' %tkn%''+char(13)+''|''
                        +char(9)+''%tkn%,''

    if @dbg=1 exec sp__elapsed @t out,''-- start convert to unicode:''
    select @i=1,@n=dbo.fn__str_count(@keywords,''|''),@m=dbo.fn__str_count(@replacers,''|'')
    while (@i<=@n)
        begin
        select @keyword=dbo.fn__str_at(@keywords,''|'',@i),@i=@i+1
        select @j=1
        while (@j<=@m)
            begin
            select @exp=dbo.fn__str_at(@replacers,''|'',@j),@j=@j+1
            select @replacer=replace(@exp,''%tkn%'',@keyword)
            select @replaced=replace(@exp,''%tkn%'',''n''+@keyword)
            -- exec sp__printf ''replacer=%s   replaced=%s'',@replacer,@replaced
            update #src set line=replace(line,@replacer,@replaced)
            end -- while replacers
        end -- while keywords

    -- special replacement
    update #src set line=replace(line,''nvarchar(4000)'',''nvarchar(4000)'')
    update #src set line=replace(line,''nchar(4000)'',''nchar(4000)'')
    if @dbg=1 exec sp__elapsed @t out,''-- end in:''
    /*
    it looks like 60 times slower and difficult do find a correct regex
    update #src set line=dbo.fn__RegexReplace(''(varchar|\[nvarchar])'','' nvarchar '',line,1,1)
    if @dbg=1 exec sp__elapsed @t out,''-- varchar-->nvarchar:''
    update #src set line=dbo.fn__RegexReplace(''(char|\[nchar])'','' nchar '',line,1,1)
    if @dbg=1 exec sp__elapsed @t out,''-- char-->nchar:''
    update #src set line=dbo.fn__RegexReplace(''(text|\[ntext])'','' ntext '',line,1,1)
    if @dbg=1 exec sp__elapsed @t out,''-- text-->ntext:''
    update #src set line=replace(line,'' nvarchar(4000)'','' nvarchar(4000)'')
    if @dbg=1 exec sp__elapsed @t out,''-- nvc8000-->4000:''
    */
    end

-- remove while line on top and bottom
if (@normalize & 8)=8
    begin
    select @i=min(lno),@n=max(lno) from #src
    while (@i<=@n)
        begin
        select @line=coalesce(line,'''') from #src where lno=@i
        if @line in ('''',@crlf,''GO'') delete from #src where lno=@i else break
        select @i=@i+@step
        end
    while (@n>=@i)
        begin
        select @line=coalesce(line,'''') from #src where lno=@n
        if @line in ('''',@crlf,''GO'') delete from #src where lno=@n else break
        select @n=@n-@step
        end
    end -- normalze 8

-- remove []
if (@normalize & 16)=16
    update #src set line=dbo.fn__RegexReplace(''(\[|])'','''',line,1,1)

-- go..go
if (@normalize & 32)=32
    print ''todo''

-- remove go
if (@normalize & 64)=64
    update #src set line=null where line=''GO''

goto ret

help:
select @msg =''@normalize can be a mix of:\n''
            +''\t1   delete null lines\n''
            +''\t2   remove collate and contraint clausees\n''
            +''\t4   upsize to unicode data type\n''
            +''\t8   remove white line on top and bottom\n''
            +''\t16  remove [] parentesis\n''
            +''\t64  remove GO lines (not compatible with memory compiling)\n''
exec sp__usage @proc,@extra=@msg
select @msg=null

ret:
if not @msg is null exec sp__printf @msg
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_reduce:

-- ========================================================= sp__script_refactor
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_refactor',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100328
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_refactor') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_refactor') with nowait
        goto skip_sp__script_refactor
        end
    if @ver>100328
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_refactor') with nowait
        goto skip_sp__script_refactor
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_refactor') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_refactor'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_refactor]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:100328\s.zaglio:
*/
create proc sp__script_refactor
as
begin
print ''http://www.dpriver.com/pp/sqlformat.htm''
end -- sp__script_refactor'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_refactor:

-- =========================================================== sp__script_review
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_review',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_review') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_review') with nowait
        goto skip_sp__script_review
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_review') with nowait
        goto skip_sp__script_review
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_review') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_review'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_review]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    v:091018\s.zaglio: help programmer to change code dinamically&sistematically
    g:utility
*/
CREATE proc [dbo].[sp__script_review]
    @old_code sysname=null,
    @new_code sysname=null,
    @objs sysname=''%''
as
begin

-- replace old g: tag
-- sp__script_review ''%g:sp__group%''''utility'''',''''%'''''',''%g:utility''
-- add g:tag
-- sp__script_review ''%*/'',''%g:utility{crlf}*/'',@objs=''%[_][_]%''

-- sp__script_review ''%exec%sp__elapsed%''''-- SP_SYNC_TABLES:%'''''',
--                   ''%exec%sp__elapsed%''''-- %s:%'''''',@proc
-- sp__script_review ''%exec%sp__printf%''''-- SP_SYNC_TABLES:%'''''',
--                   ''%exec%sp__printf%''''-- %s:%'''''',@proc

print ''programming aid procedure: to do see content''
end--proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_review:

-- ============================================================== sp__script_run
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_run',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140108.1506
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_run') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_run') with nowait
        goto skip_sp__script_run
        end
    if @ver>140108.1506
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_run') with nowait
        goto skip_sp__script_run
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_run') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_run'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_run]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:download,web,get,internet
    v:140108.1506\s.zaglio: moved @dbs before @uri for fast db search
    v:131215\s.zaglio: @uri -> @src
    v:131208.0900\s.zaglio: refined load from smb
    v:130531\s.zaglio: excl. sys dbs and test, removed try-catch
    r:130529\s.zaglio: added dbs option
    r:130416\s.zaglio: added alter opt
    r:130415\s.zaglio: adding unpre opt
    r:130202\s.zaglio: run a script from @uri
    t:
        sp__script_run
            ''forms|tasks'',
            ''i:\Documenti\dropbox\PAE\download\utility.sql''
            ,@dbg=1
    t:sp__script_run ''forms|tasks'',''\\localhost\xch\utility.sql'',@dbg=1
    t:sp__script_run ''forms|tasks'',''http://localhost/downloads/utility.sql'',@dbg=1
*/
CREATE proc sp__script_run
    @dbs sysname = null,
    @src nvarchar(max) = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    -- @run bit,
    @unpre bit,@alter bit,
    @crlf nvarchar(2),@cr nchar(1),@lf nchar(1),
    @i int,@n int,@j int,               -- index, counter
    -- options
    -- @sel bit,@print bit,             -- select and print option for utils
    @uri nvarchar(1024),
    @all_db nvarchar(4000),             -- option
    @db sysname,
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    -- @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
    --    |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @alter=charindex(''|alter|'',@opt),
    @crlf=crlf,@cr=cr,@lf=lf,
    @src=nullif(@src,''''),
    @uri=ltrim(rtrim(isnull(left(@src,1024),''''))),
    @db=db_name(),
    @dbs=nullif(@dbs,''''),
    @end_declare=1
from fn__sym()

if charindex(@cr,@uri)>0 select @uri=left(@uri,charindex(@cr,@uri)-1)

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if @dbs is null and @uri is null goto help

-- =============================================================== #tbls init ==

-- ===================================================================== body ==
select @dbs=isnull(@dbs,@db)

select @all_db=isnull(@all_db+'','','''')+name
-- select *
from sys.databases
cross apply fn__str_table(replace(@dbs,'','',''|''),''|'')
where name like token
and not name in (''master'',''tempdb'',''model'',''msdb'',''Resource'',''Distribution'')
and left(name,12)!=''ReportServer''
order by name

/*
    t:sp__script_run ''%''
    t:sp__script_run ''forms,ruler,tasks'',
                     ''I:\Documenti\dropbox\PAE\download\utility.sql'',@dbg=1
    t:sp__script_run ''forms,ruler,tasks,xx''
*/

if (isnull(@all_db,'''')!='''' and @all_db!=@db) or @dbg>0
    exec sp__printf ''-- to execute on:\n%s'',@all_db

if isnull(@all_db,'''')='''' raiserror(''no dbs found'',16,1)
if dbo.fn__str_count(@all_db,'','')<dbo.fn__str_count(@dbs,'','')
    raiserror(''selected dbs are less than filters'',16,1)

-- list only dbs
if @uri is null goto ret

-- check king of uri
if left(@uri,6)=''ftp://'' raiserror(''ftp not yet supported'',16,1)
if @uri like ''\\%@%'' raiserror(''smb path with uid&pwd not yer supported'',16,1)

if patindex(''http%://%'',@uri)>0
    begin
    if @dbg>0 exec sp__printf ''-- getting via web''
    exec sp__web @uri,@rsp=@src out,@rcq=''get''
    if charindex(''<pre>'',@src)>0
    and (charindex(''/>'',@src)>0 or charindex(''</pre>'',@src)>0)
        select @unpre=charindex(''|unpre|'',@opt)

    -- select @src=replace(@src,@crlf+@crlf,@crlf)
    if @unpre=1
        begin
        /*
        sp__script_run
            ''http://xoomer.virgilio.it/stezagl/io/eng/my_works/source/sp_source/code_sql/fn__at.htm'',
            @dbg=1,@opt=''unpre|alter''
        */
        select @i=charindex(''<pre '',@src),@j=charindex(''>'',@src,@i),@i=@j+1
        select @j=charindex(''</pre>'',@src)
        select @src=substring(@src,@i,@j-@i)
        while left(@src,1) in (@cr,@lf) select @src=substring(@src,2,len(@src))
        while right(@src,1) in (@cr,@lf) select @src=left(@src,len(@src)-1)
        end
    select @uri=null
    end

-- file /uri) path to windows path
if left(@uri,8)=''file:///''
    select @uri=replace(substring(@uri,9,1024),''/'',''\'')

if patindex(''%:%\%'',@uri)>0 or left(@uri,2)=''\\''
    begin
    if @dbg>0 exec sp__printf ''-- reading file %s'',@uri
    -- not the faster but read 2MB in 7 seconds on localmachine
    select @src=''''
    exec sp__file_read_stream @uri,@out=@src out

    -- sp__file_read_stream ''I:\Documenti\web_sites\PAE\download\utility.sql''

    select @n=len(@src)
    if @dbg>0 exec sp__printf ''-- loaded %d chars'',@n
    select @uri=null
    end

if @alter=1
    begin
    select @i=charindex(''create function'',@src)
    if @i=0 select @i=charindex(''create proc'',@src)
    if @i=0 select @i=charindex(''create procedure'',@src)
    if @i=0 select @i=charindex(''create view'',@src)
    if @i>0 select @src=left(@src,@i-1)+''alter ''+substring(@src,@i+7,len(@src))
    end

-- ##########################
-- ##
-- ## execute script
-- ##
-- ########################################################
if @src is null goto err_ems

-- if is a direct code
if not @uri is null
    begin
    -- remove initial returns
    while left(@src,1) in (@cr,@lf) select @src=stuff(@src,1,1,N'''')
    -- encapsulate
    select @src=''exec(''''''+replace(@src,'''''''','''''''''''')+'''''')''
    end

if @dbg>0 exec sp__printsql @src

declare cs cursor local for select token from fn__str_table(@all_db,'','')
open cs
while 1=1
    begin
    fetch next from cs into @db
    if @@fetch_status!=0 break

    if (isnull(@dbs,'''')!='''' and @dbs!=@db) or @dbg>0
        exec sp__printframe ''updating %s'',@db

    if @dbg=0
        begin
        exec(''use [''+@db+'']''+@crlf+@src)
        end
    else
        exec sp__printf ''use [%s] ...'',@db

    end -- cursor cs
close cs
deallocate cs

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    load a script from a file or a url or a remote server or a contant
    and execute it on local db or on multiple dbs

Parameters
    @dbs    multi like expression separated by comma or pipe
            where run the script
    @src    can be the source code of script
            or the path of the file or web address
            %:% or %\% or file:///% is a file
            \\% or %:%@\\% is a smb file            (not yet supported)
            ftp://[usr:pwd@]... is a ftp file       (not yet supported)
    @opt    options
            alter       replace create function/view/proc with alter
    @dbg    debug level
            1   show basic info and do not execute the script

Examples
    sp__script_run "%"                  -- list all dbs (w.out sys.dbs)
    sp__script_run "%tst%|%bak%"        -- list all dbs that likes tst or bak
    -- run script.sql in all selected dbs
    sp__script_run "%tst%|%bak%","c:\script.sql"
    -- get the script from internet and run into local db
    sp__script_run @src="http\\site.com\utility.sql"
    -- run constant script over all (non system) dbs
    sp__script_run "%","select db_name()"
''

select @ret=-1
goto ret

-- =================================================================== errors ==
err_ems:    exec sp__err ''empty script'',@proc                           goto ret

-- ===================================================================== exit ==
ret:
return @ret
end  -- proc sp__script_run'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_run:

-- =========================================================== sp__script_select
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_select',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130518
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_select') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_select') with nowait
        goto skip_sp__script_select
        end
    if @ver>130518
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_select') with nowait
        goto skip_sp__script_select
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_select') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_select'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_select]

begin try
exec dbo.sp_executesql @statement = N'/*  Leave this unchanged doe MS compatibility
    l:see LICENSE file
    g:utility,script
    v:130518\s.zaglio: select objects for scripting purpose
    t:sp__script_select ''utility'',@dbg=1
    t:sp__script_select ''utility''
    n:https://code.google.com/p/jquerycsvtotable/
    n:http://tablesorter.com/docs/
*/
CREATE proc [dbo].sp__script_select
    @grps_objs  nvarchar(4000) =null,
    @exclude    sysname =null,
    @include    sysname =null,
    @opt        sysname =null,
    @dbg        int     =0
as
begin try
set nocount on

declare
    @proc sysname,@ret int,
    @e_msg nvarchar(4000),@e_p1 sysname,@e_p2 sysname,
    @i int,@aut sysname -- derived parameter

select @proc=object_name(@@procid),@ret=0,
       @opt=dbo.fn__str_quote(coalesce(@opt,''''),''|'')

-- ================================================================ param chk ==

if @grps_objs is null goto help

if charindex(''|@'',@opt)>0
    select @aut=dbo.fn__str_between(@opt,''|@'',''|'',default)

-- ============================================================= declarations ==
if object_id(''tempdb..#info'') is null
    -- this table will contain all objects to script
    create table #info (
        obj_id int,obj sysname,tag nvarchar(4),
        val1 nvarchar(4000),val2 nvarchar(4000),val3 nvarchar(4000)
        )

if object_id(''tempdb..#grp_objs'') is null
    create table #grp_objs(
        obj_id int null,
        obj sysname,
        xt nvarchar(2) null,
        tag nvarchar(16) default(''v''),
        aut sysname,
        ver nvarchar(16) null,
        [des] sysname null,
        grp1 sysname null default(''''),
        grp2 sysname null default(''''),
        grp3 sysname null default(''''),
        core bit default(0),        -- basic obj used to define others
        [drop] sysname null,
        ord int,
        match bit,
        )

-- ===================================================================== body ==

-- groups
insert #info(obj_id,obj,tag,val1,val2,val3)
select
    obj_id,obj,tag,
    cast(val1 as nvarchar(4000)),
    cast(val2 as nvarchar(4000)),
    cast(val3 as nvarchar(4000))
from dbo.fn__script_info(null,''g'',default)

select ''grps'' tbl,* from #info

-- apply group filter
if charindex('','',@grps_objs)>0
    delete obj
    from #info obj
    join dbo.fn__str_table(@grps_objs,'','') fil
    on not token in (obj.val1,obj.val2,obj.val3)
    where obj.tag in (''v'',''r'')

select ''grps filtered'' tbl,* from #info

-- deprecated
insert #info(obj_id,obj,tag,val1,val2,val3)
select
    obj_id,obj,tag,
    cast(val1 as nvarchar(4000)),
    cast(val2 as nvarchar(4000)),
    cast(val3 as nvarchar(4000))
from dbo.fn__script_info(null,''d'',default)

select ''deprecated'' tbl,* from #info where tag=''d''

-- version or release
insert #info(obj_id,obj,tag,val1,val2,val3)
select
    obj_id,obj,tag,
    cast(val1 as nvarchar(4000)),
    cast(val2 as nvarchar(4000)),
    cast(val3 as nvarchar(4000))
from dbo.fn__script_info(null,''vr'',0)

select ''vr'' tbl,* from #info where tag in (''v'',''r'')

-- apply objects filter
if charindex(''|'',@grps_objs)>0
    delete obj
    from #info nfo
    where nfo.tag in (''r'',''v'')
    and nfo.obj in (select token from dbo.fn__str_table(@grps_objs,''|''))

select ''vr filtered'' tbl,* from #info where tag in (''v'',''r'')

-- remove deprecated
delete vr
from #info vr
join #info d
on vr.obj=d.obj
where vr.tag in(''v'',''r'') and d.tag=''d''

select ''vr - deprecated'' tbl,* from #info where tag in (''v'',''r'')

if @dbg>1 select * from #info

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    select objects to script; used by sp__script_group or other utility

Parameters
    @grps_objs  single group to script
                * the group is identified by tag G:
                * tag G support multiple groups separated by comma (",")
                * if contain a %, select names instead of group
                * if contain *, select also tables (with idxs,fkeys,trs)
                * SYS group, script S objects from fn__script_sysobjs
                * obj1|obj2|... direct script only obj1,obj2,...
                * grp1,grp2,... script objs that belongs to grp1 or ...

    @out    can be a path where out a single file or multiple files
            (extension is .sql or .htm depending on @opt)
            %grp% will be replaced with group name and create a unique file
            %obj% will replaced with obj name and create multiple files
            %t will be replaced with YYMMDD_HHMMSS
            %temp% will be replaced with windows user temp directory

    @opt    options
            html        out as html
                        (if out to multiple files, an index.htm will be created)
            prefix:path prefix path to link in index.html (prefix:./code_sql/)
            nochk       do not add check version
            bin         return results as compressed binary string
            @user       filter of objects of "user"

    @dbg    1 list selected objects and exit
            2 print and do not out to files

    @exclude is a "like" expression for post exclusion of objects
    @include is a "like" expression for pre inclusion of objects

Notes
    * an object that end with group name and "_setup", is considered the setup
      store procedure of the group, scripted 1st and executed immediatelly
    * if sp__script_store and sp__script_group are in R tag, cannot script
    * if object "%_%groupname%_setup" is in R, scripting is aborted

Examples
    -- normal use
    exec sp__script_group ''''utility''''

    -- special out to file with html format and index
    exec sp__script_group ''''script'''',@out=''''%temp%\%obj%'''',@opt=''''index''''

    -- replace macros
    create table #vars (id nvarchar(16),value nvarchar(4000))
    insert #vars select ''''%license%'''',''''test replacements''''
    exec sp__script_group ''''script''''
    drop table #vars

##########################
##
## SCRIPT GROUP STEPS
## ==================
##
## if script to console or into single file:
##   drop deprecated objects
##   check version
##     drop object
##     re-create
##
## is script "utility" group:
##   if exists uninstall db trigger
##   eventually re-create scripting core objects
##   drop deprecated objects
##   check version
##     drop object
##     re-create
##   if exists install db trigger and upgrade if necessary
##
########################################################

''
select distinct grp grps
from (
    select val1 grp from #info nfo where nfo.tag=''g''
    union
    select val2 grp from #info nfo where nfo.tag=''g''
    union
    select val3 grp from #info nfo where nfo.tag=''g''
    ) grps
where not grp is null
order by 1

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_select'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_select:

-- ======================================================== sp__script_sign_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_sign_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131123
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_sign_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_sign_test') with nowait
        goto skip_sp__script_sign_test
        end
    if @ver>131123
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_sign_test') with nowait
        goto skip_sp__script_sign_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_sign_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_sign_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_sign_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131123\s.zaglio: adapted to new fn__script_sign
    v:130923\s.zaglio: added help of fn__Script_sign
    v:130730,130729\s.zaglio: test fn__script_sign
    t:select dbo.fn__script_sign(''fn__script_info_tags'',null)   -- 105529698.0000
    t:select dbo.fn__script_sign(''fn__script_info_tags'',1)      -- 131129.0000
    t:select dbo.fn__script_sign(''fn__script_info_tags'',4)      -- 1796022540.0000
    t:select dbo.fn__script_sign(''fn__script_info_tags'',8)      -- 105529698.0000
*/
CREATE proc sp__script_sign_test
as
begin
set nocount on
declare
    @ret int,@proc sysname
select @ret=0,@proc=object_name(@@procid)
declare
    @id int,@obj sysname,@detail tinyint,
    @result numeric(14,4),@des sysname,
    @new_result numeric(14,4),@st sysname

declare @t table(
    id int,
    obj sysname,
    detail tinyint,
    result numeric(14,4),
    des sysname
    )
insert @t
select 1,''tst_fn__script_sign'',null,141296445.0000,''rows only(!)''
union
select 2,''tst_fn__script_sign'',1,755913805.0000,''with idx''
union
select 3,''tst_fn__script_sign'',4,2136238363.0000,''as (!)-names''
union
select 4,''tst_fn__script_sign'',8,577207619.0000,''with charset&collate''

union
select 5,''sp__script_sign_test'',null,0.0000,''header''
union
select 6,''sp__script_sign_test'',1,131123.0000,''body''
union
select 7,''sp__script_sign_test'',4,0.0000,''types only''
union
select 8,''sp__script_sign_test'',8,0.0000,''with charset&collate''

union
select 9,''fn__script_sign_test'',null,974999305.0000,''header''
union
select 10,''fn__script_sign_test'',1,131123.0000,''body''
union
select 11,''fn__script_sign_test'',4,443965265.0000,''types only''
union
select 12,''fn__script_sign_test'',8,974999305.0000,''with charset&collate''

exec sp__printf ''-- test fn__script_sign correct results''

if not object_id(''tst_fn__script_sign'') is null
    drop table tst_fn__script_sign
exec(''
create table tst_fn__script_sign(
    a int,
    b sysname collate Latin1_General_CI_AS null
    )
create index ix_tst_fn__script_sign_a on tst_fn__script_sign(a)
'')

if not object_id(''fn__script_sign_test'') is null
    drop function fn__script_sign_test
exec(''/*    leave this
    v:131123\s.zaglio: test creation
    */
create function fn__script_sign_test(@a int, @b sysname)
returns table
as
return select cast(1 as int) as a,cast(''''b'''' as sysname) as b
union  select @a,@b
'')

declare cs cursor local for
    select id,obj,detail,result,des
    from @t
open cs
while 1=1
    begin
    fetch next from cs into @id,@obj,@detail,@result,@des
    if @@fetch_status!=0 break

    select @new_result=dbo.fn__script_sign(@obj,@detail)
    if @new_result=@result
        select @st=''ok'' else select @st=''ko''
    exec sp__printf ''test %d:%s, %s, expected:%s, given:%s'',
                    @id,@st,@obj,@result,@new_result
    if @st=''ko'' select @ret=1

    end -- cursor cs
close cs
deallocate cs

if not object_id(''tst_fn__script_sign'') is null
    drop table tst_fn__script_sign
if not object_id(''fn__script_sign_test'') is null
    drop function fn__script_sign_test

if @ret!=0 exec @ret=sp__err ''test failed'',@proc

exec sp__printf ''''
exec sp__usage @proc,''
Scope
    test fn__script_sign and give help for fn

Parameters
    @obj    name of object
    @detail specification of what it calculate the signature
            * details for table/sp/fn/views
            bit_val  meaning
            0/null   table columns or params of sp/fn, without defaults
            1        table columns with index or params of sp/fn with body
            2        table columns or params of sp/fn without names, without defaults
            4        table columns with index or params of sp/fn without names
            8        same as previous with charset&collate info
            * details for jobs
            0/null   names/status of jobs
            1        names/status of jobs with names,commands,flags,outfile of steps
            2        status of jobs
            6        status of jobs with commands,flags,outfile of steps without names
''

return @ret
end -- sp__script_sign_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_sign_test:

-- ============================================================ sp__script_store
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_store',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131006.1200
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_store') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_store') with nowait
        goto skip_sp__script_store
        end
    if @ver>131006.1200
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_store') with nowait
        goto skip_sp__script_store
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_store') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_store'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_store]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:131006.1200\s.zaglio:refactor and changed data to store
    v:131002.1100,131001\s.zaglio:better messages;test for more dependant objs
    v:130908;130731.1000\s.zaglio:warning "in the future";changed newer/older warning
    v:130712\s.zaglio:disable test of 130604 on alter
    v:130606\s.zaglio:improved warning on test of bad v-r tags
    v:130604\s.zaglio:test for more than 2 months older release
    v:130317;120919\s.zaglio:a bug near fn__context_info use;tested script_act
    v:120906;120905\s.zaglio:around script_act;added refer to "script_act"
    v:120823;120731\s.zaglio:added chk if not complete header and dis,dbg options,#!@msg_fl
    r:120724;120523\s.zaglio:about messages;removed range 0-65535 and tested future release
    v:120518.1800\s.zaglio:adapted to new fn__script_sysobjs
    v:120509;120503\s.zaglio:about help and debug;adopted skip of range 0-65535 and uppers
    v:120223;1140;120213\s.zaglio: a overflow near release;adapted to new fn__script_events
    v:120208;120207\s.zaglio:replaced identity;adapting to new log_ddl
    v:120126;111205\s.zaglio:reversed means of @msg option,added context info comunication
    v:110921;110824\s.zaglio:added skip of maintenance code and trim;removed use of try/c.
    v:110624\s.zaglio:log_ddl.id is no longer a progressive and utcdate
    v:110622\s.zaglio:modified host separator \ with _
    v:110621\s.zaglio:added msg for every store and more help
    v:110527\s.zaglio:added exclusions of sysobjs_ and _sysobjs
    v:110510;110504\s.zaglio:added specific skips;added chk of diff. users
    v:110418;110415\s.zaglio:added skip of utilities;adapted to log_ddl
    v:110325;110324\s.zaglio:add check of tids and flags;better msg, no prop.if same ver
    v:110323.1818;110323\s.zaglio:add ver. overwr. test;exclus. of some event from test
    v:110322;110321\s.zaglio:added release;called by tr__script_trace_db
    t:sp__script_trace
    t:sp__script_history
*/
CREATE proc sp__script_store
    @et sysname = null,                 -- event type
    @obj sysname = null,
    @sql nvarchar(max) = null,
    @opt sysname = null,                -- if called by command line
    @dbg int = null
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on

declare
    @proc sysname, @err int, @ret int,@no_opt varchar(1)

select
    @proc=object_name(@@procid), @err=0, @ret=0,
    @dbg=isnull(@dbg,0), @no_opt=''''

-- dependencies test
if @obj=''fn__script_info_tags'' goto ret
if @obj=''log_ddl'' and @et=''drop_table'' goto err_tbl

if object_id(''tids'') is null
or object_id(''fn__sym'') is null
or object_id(''fn__crc16'') is null
or object_id(''fn__str_quote'') is null
or object_id(''fn__context_info'') is null
or object_id(''fn__servername'') is null
or object_id(''fn__script_info_tags'') is null
or object_id(''flags'') is null
or object_id(''log_ddl'') is null
or object_id(''fn__script_sysobjs'') is null
    goto wrn_dep

select @opt=case
            when @opt is null  then @no_opt
            else dbo.fn__str_quote(@opt,''|'')
            end

-- ##########################
-- ##
-- ## transaction into try/catch into trigger
-- ##
-- ########################################################
/*
    begin try
    save tran ...
    begin tran ...
    unfortunatelly into a trigger any form of sub transaction is not possibile
    because rollback automatically even if the error is managed
    The procedure must be perfect!
*/

-- ================================================================= declares ==

declare
    @time datetime,
    @ms int,                                        -- milliseconds
    @i int,                                         -- index var
    @tmp nvarchar(max),

    @eq_body bit,                                   --
    @udt datetime,                                  -- last update time
    @flags smallint,
    @fver smallint,
    @pvft bit,                                      -- has version header

    @srv sysname,
    @app nvarchar(256),
    @usr nvarchar(256),
    @host nvarchar(256),
    @prev_usr nvarchar(256),
    @cmt nvarchar(4000),

    @id int,
    @start_id int,
    @counter_id int,
    @app_id int,
    @usr_id int,
    @host_id int,
    @event_id int,
    @obj_id int,
    @db_id int,
    @srv_id int,
    @parent_id int,

    -- hashes for faster search
    @app_hash int,
    @usr_hash int,
    @obj_hash int,
    @host_hash int,
    @db_hash int,
    @srv_hash int,

    -- type of records (tids)
    @tcnt tinyint,
    @tsql tinyint,
    @tdb tinyint,
    @tusr tinyint,
    @tobj tinyint,
    @tapp tinyint,
    @tsrv tinyint,
    @tev tinyint,
    @thost tinyint,
    /*  log_ddl content (select * from log_ddl)
        tid     rid         pid         txt             rel             flags
        ======= =========== =========== =============== =============== ======
        cnt     last id                 "id counter"
        svr     0           0           svr name
        db      svr id      0           db name
        obj     db id       0           obj name
        sql     obj_id      host id     code            last release    flg.ver
        host    svr id                  host name
        usr     host id     0           usr name
        app     host id                 app name
    */

    @tag nchar(1),                                  -- tag R or V
    @db sysname,
    @rel bigint,
    @old bigint,
    @old_flags smallint,
    @today_rel int,                                 -- calc. from date
    @dt datetime,                                   -- to calc cur_rel
    @prev_usr_id int,
    @hh nvarchar(32), @mi nvarchar(32),
    @info sysname,

    -- session config
    @msg_fl smallint,
    @msg nvarchar(1024),
    @moff sysname,@mon sysname,                     -- msg on/off codes ...
    @dis sysname,                                   -- disable
    @dbgswc sysname,                                -- debug switch

    @et_cmd nvarchar(32),                           -- right split of @et
    @et_cmd_typ nvarchar(32),                       -- left split of @et

    -- options
    @opt_dbg bit,                                   -- switch debug mode on/off
    @opt_dis bit,                                   -- disable store
    @opt_ena bit,                                   -- enable store
    @opt_moff bit,                                  -- message off
    @opt_mon bit,                                   -- message on
    @opt_mdef bit,                                  -- only system messages

    -- debug info
    @dbg_nfo1 sysname,
    @dbg_nfo2 sysname,
    @dbg_nfo3 sysname,
    @dbg_nfo4 sysname,

    @end_declare bit

-- ===================================================================== init ==

select
    @time  =getdate(),
    @dis   =@proc+'':disable'',
    @dbgswc=@proc+'':debug'',
    @mon   =@proc+'':message_on'',
    @moff  =@proc+'':message_off'',
    @msg_fl=case
            when dbo.fn__context_info(@mon)>0 then 1
            when dbo.fn__context_info(@moff)>0 then -1  -- OFF win on ON
            else 0
            end

-- options
if @opt!=@no_opt
    select
        @opt_dbg  = charindex(''|dbg|'',@opt),
        @opt_dis  = charindex(''|dis|'',@opt),
        @opt_ena  = charindex(''|ena|'',@opt),
        @opt_moff = charindex(''|moff|'',@opt),
        @opt_mon  = charindex(''|mon|'',@opt),
        @opt_mdef = charindex(''|mdef|'',@opt)

-- debug info
if @dbg=1
    select
        @dbg_nfo1=''@tag=%s, @flags=%s, @tmp=%s'',
        @dbg_nfo2=''@hh=%s,@mi=%s,@rel=%s,@cur=%s'',
        @dbg_nfo3=''tag=%s,rel=%s,usr=%s'',
        @dbg_nfo4=''typ=%s, obj=%s, hast=%d, pid=%d''

-- ========================================================= param formal chk ==

if (@et is null or @obj is null or @sql is null)
    begin
    if @opt!=@no_opt
        begin
        if @opt_dbg=1
            begin
            -- debug on/off
            if dbo.fn__context_info(@dbgswc)>0
                begin
                exec sp__context_info @dbgswc,@opt=''del''
                print @proc+'': debug disabled''
                end
            else
                begin
                exec sp__context_info @dbgswc
                print @proc+'': debug enabled''
                end
            end
        if @opt_dis=1
            begin
            -- disable
            exec sp__context_info @dis
            end
        if @opt_ena=1
            begin
            -- enable
            exec sp__context_info @dis,@opt=''del''
            end
        if @opt_moff=1
            begin
            -- system and normal messages off
            exec sp__context_info @mon,@opt=''del''
            exec sp__context_info @moff
            end
        if @opt_mon=1
            begin
            -- all messages on
            exec sp__context_info @moff,@opt=''del''
            exec sp__context_info @mon
            end
        if @opt_mdef=1
            begin
            -- default: print only system messages and when obj has tag
            exec sp__context_info @mon ,@opt=''del''
            exec sp__context_info @moff,@opt=''del''
            end
        goto ret
        end

    goto help
    end -- no params given

-- check for particular situation that skip store of script

if dbo.fn__context_info(@dbgswc)>0 and @dbg=0 select @dbg=1
if dbo.fn__context_info(@dis)>0 goto ret

select @sql=ltrim(rtrim(@sql))

select @tmp=left(@sql,512)

if charindex(''[%group%]'',@tmp)>0 goto err_hdr
if charindex(''[%keywords%]'',@tmp)>0 goto err_hdr
if patindex(''%\[%]%[%]:%'',@tmp)>0 goto err_hdr

-- skip specific maintenance
if @sql like ''ALTER INDEX % REORGANIZE WITH %'' goto ret

-- skip MSOffice and specific application or system obj
-- select * from dbo.fn__script_sysobjs((select obj from tids))
if len(@obj)=3 goto ret             -- generically the sys obj are or 3 letters
if left(@obj,3) in (''dt_'') goto ret

if @dbg=1
    begin
    raiserror(@proc,10,0) with nowait
    print ''obj:''+isnull(@obj,''???'')
    print ''sql:''+isnull(@sql,''???'')
    end

select
    @start_id=  power(-2,31),
    @dt=        getdate(),
    @today_rel= cast(convert(sysname,@dt,12)+
                     left(replace(convert(sysname,@dt,8),'':'',''''),4)
                     as int
                    ),
    @tcnt=  1,
    @tsrv=  tids.srv,
    @tdb=   tids.db,
    @tusr=  tids.usr,
    @tapp=  tids.app,
    @tsql=  tids.code,
    @tobj=  tids.obj,
    @tev=   tids.ev,
    @thost= tids.host,
    @flags= 0,
    @srv=   upper(dbo.fn__servername(null)),
    @db=    upper(db_name()),
    @app=   upper(left(app_name(),256)),
    @host=  upper(left(system_user+''@''+host_name(),256)),
    @usr=   null,
    @obj=   upper(@obj),
    @srv_hash=dbo.fn__crc32(@srv),
    @db_hash =dbo.fn__crc32(@db),
    @app_hash=dbo.fn__crc32(@app),
    @obj_hash=dbo.fn__crc32(@obj),
    @host_hash=dbo.fn__crc32(@host),
    @fver=  flg.ver,
    @i=charindex(''_'',@et),
    @et_cmd = left(@et,@i-1),
    @et_cmd_typ = substring(@et,@i+1,128),
    @pvft=  case                                -- has version header
            when @et_cmd_typ
                 in (''proc'',''view'',''function'',''trigger'')
            then 1
            else 0
            end,
    @udt=   getutcdate()
from tids,flags flg

-- excludes system objects derivated (prefix, postfix, same)
if exists(
    select top 1 null
    from dbo.fn__script_sysobjs(@tobj)
    where cod+''_''=left(@obj,4)
    or ''_''+cod=right(@obj,4)
    or cod=@obj
    )
    goto ret

-- ============================================= check header and old version ==

select @tmp=null
select top 1
    @tag=tag,
    @tmp=convert(nvarchar,val1),
    @usr=convert(nvarchar,val2),
    @cmt=convert(nvarchar,val3)
from dbo.fn__script_info_tags(@sql,''rv'',0)

if not @tag is null and @msg_fl!=-1 select @msg_fl=1

if @tag=''v'' or @tag is null select @flags=@flags|@fver

if @tag in (''r'',''v'') and (isnull(@usr,'''')='''' or isnull(@cmt,'''')='''')
    goto err_hdr

-- if @cmt='''' -- no comment or omitted author

if @dbg=1 exec sp__printf @dbg_nfo1,@tag,@flags,@tmp

select @msg=''''
if @tmp is null
    begin
    if @pvft=1
        begin
        -- print dbo.fn__str_at(''alter proc'','''',2)
        select @msg=''no release info in "''+@obj+''"''
        end
    end
else
    begin
    select @i=charindex(''.'',@tmp)
    if @i=0
        select @mi=''0000'',@hh=@tmp
    else
        select  @hh=substring(@tmp,1,@i-1),
                @mi=right(''0000''+substring(@tmp,@i+1,32),4)
    if len(@hh)!=6
        select @msg=''wrong version near "%tmp%"''
    else
        begin
        if @hh+@mi like ''%[^0-9]%''
            select @msg=''bad release number near "%tmp%"''
        else
            begin
            select @rel=cast(@hh+@mi as bigint)
            if @dbg=1 exec sp__printf @dbg_nfo2,@hh,@mi,@rel,@today_rel
            if @rel>2147483648 goto err_int
            if @rel>@today_rel
                select @msg=''obj version of "%tmp%" is in the future'',
                       @tmp=@obj

            if @et_cmd=''alter''
                begin
                declare @nrel float select @nrel=abs(@rel-@today_rel)
                if (@nrel>2000000) -- two months
                    select @msg=''obj "%tmp%" newer or older than 2 months'',
                           @tmp=@obj
                end -- if @et_cmd=

            end -- if ok yymmdd
        end -- if good release info
    end -- if not @tmp/src is null

if @msg_fl=1 and @msg!='''' print @proc+'':WARNING:''+replace(@msg,''%tmp%'',@tmp)

if @dbg=1 exec sp__printf @dbg_nfo3,@tag,@rel,@usr
-- notes: when CATCH is released, error 3930 occur

-- ================================================= get/store srv/db/usr nfo ==

-- do not store utilities like sp__, fn__, ix__, ...
if substring(@obj,3,2)=''__'' goto ret

select
    @counter_id=case when l.tid=@tcnt  then id else @counter_id end,
    @srv_id=    case when l.tid=@tsrv  then id else @srv_id end,
    @db_id =    case when l.tid=@tdb   then id else @db_id  end
from log_ddl l
where l.tid in (@tsrv,@tdb,@tcnt)

if @counter_id is null
    begin
    select @counter_id=@start_id
    insert log_ddl(srv,id,tid,rid,pid,flags,txt,dt,[key])
    select @counter_id, @counter_id, 1,@counter_id+3, 0, 0, ''id counter'', @udt, 0
    end

if @srv_id is null
    begin
    select @srv_id=@start_id+1
    insert log_ddl(srv,id,tid,rid,pid,flags,txt,dt,[key])
    select @srv_id, @srv_id, @tsrv, 0, 0 as flags, 0, @srv, @udt, @srv_hash
    end

if @db_id is null
    begin
    select @db_id=@start_id+2
    insert log_ddl(srv,id,tid,rid,pid,flags,txt,dt,[key])
    select @srv_id, @db_id, @tdb, @srv_id, 0, 0, @db, @udt, @db_hash
    end

-- search or insert host_id
select @host_id=id
from log_ddl [log] -- with (index(ix_log_ddl_key))
where srv=@srv_id
and tid=@thost
and [key]=@host_hash
and [log].skey=@host
and rid=@srv_id

if @host_id is null
    begin
    update log_ddl with (updlock) set
        @host_id=rid=rid+1
    where id=@counter_id
    insert log_ddl(srv,tid,id,rid,pid,flags,[key],txt,dt)
    select @srv_id,@thost,@host_id,@srv_id,0,0,@host_hash,@host,@udt
    end

-- search obj_id
select @obj_id=id
from log_ddl [log] -- with (index(ix_log_ddl_key))
where srv=@srv_id
and tid=@tobj
and [key]=@obj_hash
and [log].skey=@obj
and rid=@db_id

if @obj_id is null
    begin
    update log_ddl with (updlock) set
        @obj_id=rid=rid+1
    where id=@counter_id
    insert log_ddl(srv,tid,id,rid,pid,flags,[key],txt,dt)
    select @srv_id,@tobj,@obj_id,@db_id,0,0,@obj_hash,@obj,@udt
    end

-- ================================================= compare with old version ==

select @event_id=id
from fn__script_sysobjs(@tev) ev
where ev.cod=@et

-- read last occurrence of same obj
-- sp__script_trace
select top 1
    @old_flags=flags,
    @eq_body=case when @sql=txt then 1 else 0 end,
    @old=rel,        -- rel/ver
    @prev_usr_id=pid
from log_ddl [log] -- with (nolock,index(ix_log_ddl_key))
where srv=@srv_id
and tid=@tsql
and [key]=@obj_hash
and rid=@obj_id
order by dt desc

-- update log_ddl set c5=1103221000,rid=1234 where id=7
-- delete from log_ddl where id=9
if @old>@rel
and @msg_fl=1
    begin
    select @msg=@proc+'': WARNING :probable version conflict''+char(13)
               +''this ''
               +substring(cast(@rel as nvarchar),1,6)+''.''
               +substring(cast(@rel as nvarchar),7,4)
               +''is older than previous ''
               +substring(cast(@old as nvarchar),1,6)+''.''
               +substring(cast(@old as nvarchar),7,4)
               +'' use sp__script_diff @obj to see differences''
    print @msg
    end
else
    begin
    if (@prev_usr_id!=@usr_id)
    and @msg_fl=1
        begin
        select top 1 @prev_usr=txt from log_ddl where id=@prev_usr_id
        select @msg=@proc+'': WARNING :previously changed by user "''
                   +isnull(@prev_usr,''(unk)'')+''".''
                   +'' Use sp__script_diff @obj to see differences.''
        print @msg
        end
    end -- prev_host

select
    @eq_body=isnull(@eq_body,0),    -- if is new:
    @old_flags=isnull(@old_flags,0) -- is a release

if @old=@rel                    -- if version/release number is equal
and @old_flags&@fver=@fver      -- and prev. store is a ver. and not a rel.
and @eq_body=0                  -- but bodies are different
and @flags&@fver=@fver          -- and the new body is a version too,
    if @msg_fl=1
        begin
        select @msg=@proc+
                   +'':WARNING:same version with different body will ''
                   +''not be distributed''
        print @msg
        end

if @eq_body != 0 goto ret

-- ====================================================== store reference ids ==

-- search or insert usr_id
if @usr is null -- if drop ... try to go back to usr from host
    begin
    select @usr_id=id,@usr=skey
    from log_ddl [log] -- with (index(ix_log_ddl_key))
    where srv=@srv_id
    and tid=@tusr
    and rid=@host_id
    if @@rowcount=0 select @usr_id=@host_id
    end
else
    begin
    select @usr_hash=dbo.fn__crc32(@usr)

    select @usr_id=id
    from log_ddl [log] -- with (index(ix_log_ddl_key))
    where srv=@srv_id
    and tid=@tusr
    and [key]=@usr_hash
    and [log].skey=@usr
    and rid=@host_id

    if @usr_id is null
        begin
        update log_ddl with (updlock) set
            @usr_id=rid=rid+1
        where id=@counter_id
        insert log_ddl(srv,tid,id,rid,pid,flags,[key],txt,dt)
        select @srv_id,@tusr,@usr_id,@host_id,0,0,@usr_hash,@usr,@udt
        end
    end

-- search or insert app

-- app info are stored as stand alone
select @app_id=id
from log_ddl [log] -- with (index(ix_log_ddl_key))
where srv=@srv_id
and tid=@tapp
and [key]=@app_hash
and [log].skey=@app
and rid=@host_id

if @app_id is null
    begin
    update log_ddl with (updlock) set
        @app_id=rid=rid+1
    where id=@counter_id
    insert log_ddl(srv,tid,id,rid,pid,flags,[key],txt,dt)
    select @srv_id,@tapp,@app_id,@host_id,0,0,@app_hash,@app,@udt
    end

-- ==================================================================== store ==

update log_ddl with (updlock) set
    @id = rid = rid+1
where id=@counter_id
insert log_ddl(
    srv,tid,id,rid,pid,flags,
    [key],txt,ev,rel,dt
    )
select
    @srv_id,@tsql,@id,@obj_id,@usr_id,@flags,
    @obj_hash,@sql,@event_id,@rel,@udt

select @i=@@rowcount
-- select @id=lid from log_ddl where id=@id
select @ms=datediff(ms,@time,getdate())
if @msg_fl=1
    print @proc+'': "''+@obj+''" computed and stored in ''+convert(sysname,@ms)
               +''ms with id ''
               +substring(dbo.fn__hex(@id),3,8)
               +'' on [''+@db+'']. See SP__SCRIPT_HISTORY.''

-- application triggers
if not object_id(''script_act'') is null
    begin
    /*  if is an index (todo:rule & constraint), search for parent */
    if @et_cmd_typ=''index''
        begin
        -- unfortunatelly drop index is not catched because
        -- do not exists anymore
        select @parent_id=object_id from sys.indexes where name=@obj
        select @obj=name,@obj_hash=dbo.fn__crc32(upper(name))
        from sys.objects o
        where object_id=@parent_id
        end
    if @dbg=1 exec sp__printf @dbg_nfo4,@et_cmd_typ,@obj,@obj_hash,@parent_id

    select @sql=null
    select @sql=isnull(@sql+char(13),'''')+txt
    from script_act
    where pid=@obj_hash
    order by idx
    if isnull(@sql,'''')!=''''
        begin
        exec(@sql)  -- use of try-catch will rollback all in case of error
        if @@error!=0 exec sp__printsql @sql
        end
    end

/*  removed because sqlagent errors
if not object_id(''sp__script_sync'') is null exec sp__script_sync @opt=''run''
*/

goto ret

-- =================================================================== errors ==
err_tbl:
raiserror(''before drop this table, disable trace db trigger'',11,1)
rollback

err_int:
select @msg=''release out of range (>214748.3648).''+
            ''Your TSQL was executed but not stored.''
raiserror (@msg,11,1)
goto ret

err_rel:
select @msg=''obj %s compiled but not stored because ''+
            ''you specified a version in the future or 2 months older:''+
            '' %s.%s VS %d''
raiserror(@msg,11,1,@obj,@hh,@mi,@today_rel)
goto ret

err_hdr:
select @msg=''WRONG HEADER: missing AUTHOR or COMMENT in tag R,V ''
           +''or %%group%%, %%keywords%%.''
raiserror (@msg,11,1)
rollback
goto ret

wrn_dep:
print @proc+'': WARNING :not executed because a base utility is absent.''
goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    Called by tr__trace or sp__trace_event,
    store info about DDL event in table LOG_DDL.
    Called by sp__script_history,
    store info about DML event in table LOG_DDL.

Notes
    * Show only messages when object has tags. See MOFF,MDEF options.
    * if a reference is present into "script_act", do that action.
      (this is like a trigger related to any action about an object)

Parameters
    @et     see db trigger
    @obj    see db trigger
    @sql    see db trigger
    @opt    options
            moff    hide all messages
            mon     show all messages
            mdef    reset to default
            dis     disable the store in case of inside error
            ena     re/enable the store
            dbg     enable debug info

''

-- check for foundamental objects presence
select @i=1,@tmp=''tids|flags|log_ddl|fn__script_sysobjs''
while 1=1
    begin
    select @obj=dbo.fn__str_at(@tmp,''|'',@i),@i=@i+1
    if @obj is null break
    if object_id(@obj) is null
        exec sp__printf ''WARNING:%s is a foundamental object'',@obj
    end

exec sp__printf ''
List prefix,postfix or object name that will be excluded by this sp
''
select @tobj=obj from tids
exec sp__select_astext
    ''select cod as sysobj from dbo.fn__script_sysobjs({1})'',
    @header=0,@p1=@tobj
select @ret=-1
ret:
return @ret
end -- drop proc sp__script_store'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_store:

-- ============================================================= sp__script_sync
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_sync',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110418
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_sync') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_sync') with nowait
        goto skip_sp__script_sync
        end
    if @ver>110418
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_sync') with nowait
        goto skip_sp__script_sync
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_sync') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_sync'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_sync]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,trace
    r:110418\s.zaglio: added check of presence of utils
    r:110321\s.zaglio: syncronize new updates
*/
CREATE proc sp__script_sync
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)

-- check if utilities are regenerating
if object_id(''fn__str_quote'') is null goto ret
if object_id(''sp__isjob'') is null goto ret
if object_id(''fn__config'') is null goto ret
if object_id(''sp__config'') is null goto ret
if object_id(''sp__job'') is null goto ret

select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==
declare @ok int
-- xp_cmdshell ''type C:\DOCUME~1\NETWOR~1\IMPOST~1\Temp\utility_sp__script_sync_log.txt''
exec @ok=sp__isjob @@spid,@dbg=0
-- select @ok=dbo.fn__isjob(@@spid)
if @ok=1 goto sync

if @opt=''||'' goto help

-- ============================================================== declaration ==
-- declare -- insert before here --  @end_declare bit
-- =========================================================== initialization ==
-- select -- insert before here --  @end_declare=1
-- ======================================================== second params chk ==
-- ===================================================================== body ==
-- if the job is running, do not execute it again
if convert(bit,dbo.fn__config(''script_sync'',0))=0
    begin
    -- lock the job launch
    -- sp__config ''%''
    exec sp__config ''script_sync'',1
    exec sp__job ''SCRIPT_SYNC'',@sp=''sp__script_sync'',@opt=''run|quiet''
    end

goto ret

sync:

-- wiat 5 seconds in case that a second run occur
print ''synchronizing...''
-- update log_ddl with (readpast) set udt=getdate()

-- exec sp__sync ''log_ddl'',@opt=''>''

waitfor delay ''00:00:05''
exec sp__config ''script_sync'',0
goto ret

-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    [write here a short desc]

Parameters
    [param]     [desc]

Examples
    [exam ple]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_sync'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_sync:

-- ========================================================== sp__script_synonym
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_synonym',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121003
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_synonym') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_synonym') with nowait
        goto skip_sp__script_synonym
        end
    if @ver>121003
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_synonym') with nowait
        goto skip_sp__script_synonym
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_synonym') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_synonym'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_synonym]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:follow,synonym,object,table,view
    v:121003\s.zaglio: follow synonym and return real object or script
*/
CREATE proc sp__script_synonym
    @synonym nvarchar(max) = null out,
    @obj sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    @i int,-- @n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @opt1 bit,@opt2 bit,
    @db sysname,@sql nvarchar(4000),
    @crlf nvarchar(2),
    @end_declare bit

-- =========================================================== initialization ==
select
    @db=isnull(parsename(@obj,3),db_name()),
    -- @opt1=charindex(''|opt|'',@opt),
    @synonym=null,
    @crlf=crlf,
    @end_declare=1
from fn__sym()

-- ======================================================== second params chk ==
if isnull(@obj,'''')='''' -- @opt=''||''
    goto help

-- ===================================================================== body ==
-- follow synonym
select @i=1
while not object_id(@obj,N''SN'') is null and @i<4
    begin
    -- exec sp__printf ''-- db=%s, obj=%s'',@db,@obj
    select @sql=''
        select @obj=base_object_name
        from [''+@db+''].sys.synonyms
        where name=parsename(@obj,1)
        ''
    exec sp_executesql @sql,N''@obj sysname out'',@obj out
    select @db=isnull(parsename(@obj,3),db_name())
    select @i=@i+1
    end

if charindex(''|path|'',@opt)>0
    select @synonym=@obj
else
    begin
    create table #src(lno int identity primary key,line nvarchar(4000))
    if @db!=db_name() insert #src select ''use [''+@db+'']''
    exec sp__script @obj
    select @synonym=isnull(@synonym+@crlf,'''')+line from #src order by lno
    drop table #src
    end -- script

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.
if @dbg=1 exec sp__printsql @synonym
goto ret

-- =================================================================== errors ==
/*
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_me1:    select @e_msg=''write here msg''                              goto err
err_me2:    select @e_msg=''write this %s'',@e_p1=@var                    goto err
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    follow synonym and return real object or script

Parameters
    @synonym    output the real object path or script
    @obj        real object or synonym
    @opt        options
                path    force return of path of object instead of source

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_synonym'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_synonym:

-- ============================================================ sp__script_table
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_table',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121010.1700
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_table') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_table') with nowait
        goto skip_sp__script_table
        end
    if @ver>121010.1700
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_table') with nowait
        goto skip_sp__script_table
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_table') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_table'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_table]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility,script
    v:121010.1700\s.zaglio: added sql92,nopkx options and sql92 output
    v:121005\s.zaglio: done previous
    v:121004\s.zaglio: a bug near order of idx with includes
    v:120919.1300\s.zaglio: a bug near idx with single fld and some includes
    v:120731.1300\s.zaglio: ix->pk
    v:120727\s.zaglio: adding #src_def
    v:120305\s.zaglio: used fn__script_idx
    v:111028\s.zaglio: moved scripting of triggers in sp__script
    v:110624\s.zaglio: bug near pkey name and blank lines
    v:110510\s.zaglio: near help
    v:110324\s.zaglio: added n/varxxxx(max) and include
    v:100919\s.zaglio: added advice about fkeys
    v:100905\s.zaglio: added go before trigger
    v:100509\s.zaglio: added more noidx,notrg,noidn options
    v:100404\s.zaglio: a bug near "non custer..." & collate in fn__sql_def.. & quote & idx sep
    r:100328\s.zaglio: called alone or from sp__script 3.0
    t:
        create table test(id int identity(10,10),
                          i int, r real,
                          f float constraint test_chk check (f<10.4),
                          s sysname,v varchar(12), nv nvarchar(12),
                          c char(10) default ''c10'', nc nchar(10),
                          nn bit not null default 0,
                          num numeric(18,1), dec decimal(18,1),
                          dt datetime constraint test_dt_def default (getdate()),
                          cf as 2*1)

        create  index [ix_test] on [test]([f], [s] desc ) on [primary]
        create  index [ix_test1] on [test]([f] desc ) on [primary]
        create  unique  index [ix_test_uk] on [test]([s]) on [primary]
        go
        create trigger tr_test_del on test for delete as print ''test''
        go
        create trigger tr_test_ins on test for insert as print ''test''
        go
        exec sp__script_table ''test''
        drop table test
    t:sp__script_table ''cfg'',@opt=''sql92'',@dbg=2
    t:sp__script_table ''log_ddl'',@opt=''sql92'',@dbg=2
*/
CREATE proc [dbo].[sp__script_table]
    @obj sysname=null,
    @opt sysname=null,
    @dbg int=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0
if @obj is null goto help

declare
    @id int,
    @db sysname,@sch sysname,@sch_id int,
    @obj_ex sysname,@obj_in sysname,@q nvarchar(2),
    @sql nvarchar(4000),@indent sysname,
    @blank nvarchar(1),@pos int,
    @i int,@n int,@idx sysname,@pk bit,@uq bit,@cl bit,
    @def bit, @src_pos int,@src_id int,
    @src_seed int,@src_incr int,
    @sql92 bit,@sqlite bit,@nofg bit,@nodbo bit,
    @noclt bit,@noidx bit,@noidn bit,@nocmt bit,@nopkx bit,
    @end_declare bit

declare @src table(lno int identity primary key,line nvarchar(4000))

select
    @indent=''    '',
    @opt=dbo.fn__str_quote(coalesce(@opt,''''),''||''),
    @sqlite=charindex(''|sqlite|'',@opt),
    @sql92=charindex(''|sql92|'',@opt),
    @nofg=charindex(''|nofg|'',@opt),
    @nodbo=charindex(''|nodbo|'',@opt),
    @noclt=charindex(''|noclt|'',@opt),
    @noidx=charindex(''|noidx|'',@opt),
    @noidn=charindex(''|noidn|'',@opt),
    @nocmt=charindex(''|nocmt|'',@opt),
    @nopkx=charindex(''|nopkx|'',@opt),
    @q=case when charindex(''|quote|'',@opt)>0 then ''[]'' else ''[]'' end,
    @def=isnull(object_id(''tempdb..#src_def''),0),
    @src_id=isnull(object_id(''tempdb..#src''),0),
    @src_seed=ident_seed(''#src''),
    @src_incr=ident_incr(''#src''),
    @blank=''''

if @src_id!=0
    select
        @src_pos=isnull(max(lno),@src_seed)
    from #src

select
    @db =parsename(@obj,3),
    @sch=parsename(@obj,2),
    @obj=parsename(@obj,1)

if @db is null select @db=db_name()
select @sch=[name],@sch_id=id from dbo.fn__schema_of(@obj)
select @obj_ex=quotename(@db)+''.''+coalesce(quotename(@sch),'''')+''.''+quotename(@obj)
select @id=object_id(@obj_ex)
if @id is null goto err_nof

/*
sections:
    1. usefull select to collect info into common and confortable tables
    2. apply options correction  (not performant but simple)
    3. create scripts from tables
*/

-- ========================================================================= --

-- =================================================================== tables ==
select identity(int,1,1) as row_id,0 as lrow_id,*
into #table
from fn__script_tbl(@id)
order by name
update #table set lrow_id=(select max(row_id) from #table)
-- ================================================================== columns ==
select identity(int,1,1) as row_id,0 as lrow_id,*
into #columns
from fn__script_col(@id)
order by tablename,tableowner,columnid
update #columns set lrow_id=(select max(row_id) from #columns)
-- ================================================================== indexes ==
-- declare @id int select @id=object_id(''???'')
select identity(int,1,1) as row_id,0 as lrow_id,*
into #indexes   -- drop table #indexes
from fn__script_idx(@id)
order by index_id,index_column_id

-- differences last row id on each group of included and not included flds
update idxs set lrow_id=lid
from #indexes idxs
join (
    select indexname,is_included_column,max(row_id) lid
    from #indexes
    --where is_included_column=1
    group by indexname,is_included_column
    ) ix
on idxs.indexname=ix.indexname
and idxs.is_included_column=ix.is_included_column

-- select row_id,lrow_id,indexname,columnname,is_included_column,* from #indexes

-- ========================================================================= --
-- compose source
-- remove undesired options
if @sqlite=1
    begin
    select @sql92=1
    update #columns set typename=''integer''
    where typename in (''int'',''bigint'',''tinyint'')
    update #columns set typename=''blob''
    where typename in (''uniqueidentifier'')
    -- update #columns set typename=''text''
    -- where typename typename in (''text'',''ntext'')
    end
if @sql92=1 select @nofg=1,@nodbo=1,@noclt=1,@nopkx=1
if @nofg=1
    begin
    update #table set [owner]=null
    update #indexes set parentowner=null
    end
if @nodbo=1
    begin
    update #table set [filegroup]=null
    update #indexes set [filegroup]=null
    end
if @noclt=1 update #columns set collation=null
if @noidx=1 truncate table #indexes
if @noidn=1 update #columns set [identity]=null

if @sql92=1
    begin
    if exists(select top 1 null from #columns c where c.iscomputed=1)
        goto err_cpt
    if exists(
        select top 1 null
        from #indexes
        where is_included_column=1
        )
        exec sp__printf ''-- included columns are not considered''
    end -- sql92 limits
-- sp__script ''continent'',@opt=''sql92''
-- ============================================================= script table ==

-- head
insert @src select top 1
    ''create table ''+coalesce(dbo.fn__str_quote(t.[owner],@q)+''.'','''')
                   +dbo.fn__str_quote(t.[name],@q)
                   +'' (''
-- select t.*,i.[primary] as haspk
from #table t

-- =========================================================== script columns ==

insert @src
select top 100 percent              -- exec sp__script_table ''test'',@opt=''nofg|noclt''
    @indent+dbo.fn__sql_def_col(    -- sp__usage ''fn__sql_def_col''
        c.tablename,null,
        dbo.fn__str_quote(c.columnname,@q),null,c.typename,
        c.[length],c.[precision],c.scale,
        c.allownulls,c.dridefaultname,c.dridefaultcode,c.[identity],
        c.iscomputed,c.computedtext,null/*chkname*/,
        null/*chkcode*/,c.collation,null,null)
    +case when row_id!=lrow_id then '','' else '''' end
    as line
-- select c.*,i.[primary] as haspk
from #columns c -- select * from #columns

order by columnid

-- pk inside table
if @nopkx=1
    begin
    select @pos=@src_pos+max(lno)*@src_incr from @src
    select distinct
        @idx=indexname,@pk=[primary],@uq=[unique],@cl=[clustered]
    from #indexes
    where [primary]=1
    if @def=1
        insert #src_def(xtype,cod,idx,flags)
        select ''PK'',@idx,@pos,
               @uq*flags.e+@cl*flags.f
        from flags

    insert @src
    select top 1
        @indent+'',constraint ''+dbo.fn__str_quote(indexname,@q)+'' primary key ''
        +case when @sql92=0 then
              case [clustered] when 1 then ''clustered ''
                                      else ''nonclustered '' end
         else ''''
         end
        +''(''
    from #indexes
    where indexname=@idx

    -- index columns
    insert @src
    select
        @indent+@indent
        +dbo.fn__str_quote(columnname,@q)+case descending when 1 then '' DESC '' else '' '' end
        +case when row_id!=lrow_id then '','' else '''' end
    from #indexes
    where indexname=@idx and is_included_column=0
    order by row_id

    insert @src
    select top 1
        @indent+'')''+coalesce('' on ''+dbo.fn__str_quote([filegroup],@q),'''')
    from #indexes where indexname=@idx

    end -- pk inside table

-- foot
insert @src
select '')''+coalesce('' on ''+dbo.fn__str_quote([filegroup],@q),'''')
from #table

-- ============================================================= script index ==

-- add indexes
declare @idxs table (id int identity,indexname sysname,pk bit,uq bit,cl bit)
insert @idxs
select distinct
    indexname,[primary],[unique],[clustered]
from #indexes
order by [primary] desc,[indexname]

select @i=min(id),@n=max(id) from @idxs
while (@i<=@n)
    begin

    select @idx=indexname,@pk=pk,@uq=uq,@cl=cl
    from @idxs
    where id=@i

    if @pk=1 and @nopkx=1 goto iterate

    -- blank line
    insert @src select @blank

    select @pos=@src_pos+max(lno)*@src_incr from @src

    if @def=1
        insert #src_def(xtype,cod,idx,flags)
        select case @pk when 1 then ''PK'' else ''IX'' end ,@idx,@pos,
               @uq*flags.e+@cl*flags.f
        from flags

    -- CREATE [UNIQUE] NONCLUSTERED INDEX [ix_test] ON [dbo].[test] ([f] ASC,[s] DESC) ON [PRIMARY]
    -- ALTER TABLE [dbo].[test] ADD PRIMARY KEY CLUSTERED ([id] ASC) ON [PRIMARY]

    -- head
    if @pk=1
        insert @src
        select top 1
            ''alter table ''+coalesce(dbo.fn__str_quote(parentowner,@q)+''.'','''')
                          +dbo.fn__str_quote(parentname,@q)
            +'' add constraint ''+dbo.fn__str_quote(indexname,@q)+'' primary key ''
            +case when @sql92=0 then
                  case [clustered] when 1 then ''clustered ''
                                          else ''nonclustered '' end
             else ''''
             end
            +''(''
        from #indexes
        where indexname=@idx
        order by row_id
    else
        insert @src
        select  top 1
            ''create ''
            +case [unique] when 1 then ''unique '' else '''' end
            +case when @sql92=0 then
                  case [clustered] when 1 then ''clustered ''
                                          else ''nonclustered '' end
                  else ''''
                  end
            +''index ''
            +dbo.fn__str_quote(indexname,@q)
            +'' on ''
            +coalesce(dbo.fn__str_quote(parentowner,@q)+''.'','''')
            +dbo.fn__str_quote(parentname,@q)
            +''(''
        from #indexes
        where indexname=@idx
        order by row_id

    -- body
    -- select * from #indexes order by indexname
    -- exec sp__script_table ''test''

    -- index columns
    insert @src
    select
        dbo.fn__str_quote(columnname,@q)+case descending when 1 then '' DESC '' else '' '' end
        +case when row_id!=lrow_id then '','' else '''' end
    from #indexes
    where indexname=@idx and is_included_column=0
    order by row_id
    -- order by columnname  -- wrong

    -- foot

    -- includes
    if @sql92=0
    and exists(
        select top 1 null
        from #indexes
        where indexname=@idx and is_included_column=1
        )
        begin
        insert @src select '') include (''
        insert @src
        select
            dbo.fn__str_quote(columnname,@q)+case descending when 1 then '' DESC '' else '' '' end
            +case when row_id!=lrow_id then '','' else '''' end
        from #indexes
        where indexname=@idx and is_included_column=1
        order by row_id
        end -- includes

    insert @src
    select top 1
        '')''+coalesce('' on ''+dbo.fn__str_quote([filegroup],@q),'''')
    from #indexes where indexname=@idx

    iterate:
    select @i=@i+1
    end -- loop indx

if @nocmt=0
    begin
    if @def=1
        begin
        select @pos=@src_pos+max(lno)*@src_incr from @src
        insert #src_def(xtype,cod,idx,flags)
        select ''--'',@obj,@pos,0
        end

    insert @src select @blank
    insert @src select ''/******************************************************************''
    insert @src select '' *** remember that fkeys are script aside with sp__script_fkeys ***''
    insert @src select '' ******************************************************************/''
    end -- nocmt

-- output
if @src_id!=0
    insert #src(line)
    select line
    from @src
    order by lno
else
    select line from @src order by lno

goto ret

-- =================================================================== errors ==

err_nof:
exec @ret=sp__err ''object %s not found'',
                  @proc,@p1=@obj_ex
goto ret

err_cpt:
exec @ret=sp__err ''sql92 not support computed columns'',
                  @proc,@p1=@obj_ex
goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Options for tables to script:
    default all dependencies (keys,constraint,indexs,triggers,etc.)
    nofg    no filegroup
    noclt   no collate
    noidx   no indexes
    notrg   no triggers
    noidn   no identity
    nocmt   no comment
    nodbo   no owner
    nopkx   script pk into table and not out as an index
    sql92   no owner,filegroup,collate,comment
''
select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end -- sp__script_table'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_table:

-- ========================================================= sp__script_template
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_template',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=151108
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_template') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_template') with nowait
        goto skip_sp__script_template
        end
    if @ver>151108
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_template') with nowait
        goto skip_sp__script_template
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_template') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_template'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_template]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:151108\s.zaglio:added collate database_default
    v:130728.1000\s.zaglio:use sp__script_template_compile
    v:130724\s.zaglio:doubled performance
    v:130713,130709\s.zaglio:added (duty) #tpl_cpl;better help and sp__Script_template_test
    v:130707,130519\s.zaglio:moved init of #tpl here;better info on missing tpl lines
    v:130430,130301\s.zaglio:a bug when #tpl is empty;a bug near insert #tpl_sec
    v:121229\s.zaglio:added %dt% and %uid% in unix notation
    v:120918\s.zaglio:removed exclusion tag from sub substitution and nomixed
    v:120914\s.zaglio:opt mix is now default and #tpl mix with itself''s secs
    v:120913\s.zaglio:split of #tpl_sec.line with crlf and a small bug
    v:120912,120725.1212\s.zaglio:better help;added @spring
    v:120627,120126\s.zaglio:a bug near mix (ex temp_db.#tpl_sec);#tpl_sec optional
    v:111230.1509\s.zaglio:probable stable version
    r:111228,111227\s.zaglio:debugging
    r:111223,111222\s.zaglio:auto tokens;new concept that reduce caller code
    v:111221,111220\s.zaglio:done tab management;about @tab as in&out
    v:111216\s.zaglio:@to searched after @from
    v:111115\s.zaglio:added "out" opt and removed exlusion of middle section
    v:111102\s.zaglio:added option
    v:110824\s.zaglio:better errors and src now is #tpl and #src become output
    v:110823,110822\s.zaglio:done;added @from,@to,@tab and removed @splitter
    v:110707,110706\s.zaglio:@bug near #var;added @excludes
    v:110701.1825,110601\s.zaglio:a bug near replace of ";added tokens
    v:110630\s.zaglio:help define templates and generate code
    t:sp__script_template_test
*/
CREATE proc sp__script_template
    @section  nvarchar(max) = null,
    @as       nvarchar(256) = null,
    @opt      nvarchar(256) = null,
    @tab      int           = null out,
    @tokens   nvarchar(4000)= null,
    @v1       sql_variant   = null,
    @v2       sql_variant   = null,
    @v3       sql_variant   = null,
    @v4       sql_variant   = null,
    @excludes nvarchar(4000)= null,
    @dbg      int           = null
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
if @dbg=2 create table #tpl (lno int identity,line nvarchar(4000))

-- ========================================================= param formal chk ==
declare
    @nomix bit,@isection sysname,@crlf nvarchar(2),@lcrlf int,@itab int,
    @tpl_oid int,@tpl_cpl_oid int

select
    @nomix=charindex(''|nomix|'',@opt),
    @crlf=crlf,
    @lcrlf=len(@crlf),
    @itab=isnull(@tab,0),
    @tab=isnull(@tab,0),
    @tpl_oid=object_id(''tempdb..#tpl''),
    @tpl_cpl_oid=object_id(''tempdb..#tpl_cpl'')
from fn__sym()


if @tpl_oid is null
-- or (object_id(''tempdb..#tpl_sec'') is null and @nomix=0)
    goto help

select @isection=left(@section,128)
-- ============================================================== declaration ==
declare
    @tpl binary(20),                    -- hash of template
    @sep nvarchar(32),
    @i int,@n int,@dtype int,@null nvarchar,
    @x1 int,@y1 int,@x2 int,@y2 int,@line nvarchar(4000),
    @replace_double_quotes bit,@print bit,@out_out bit,
    @scissors nvarchar(32),
    @tkn nvarchar(32),          -- token
    @oline nvarchar(4000),      -- original line
    @bos sysname,               -- begin of section
    @lo_lno int,@hi_lno int,    -- low and high line number
    @noimacro bit,
    @td nvarchar(4),            -- tokens delimiter
    @d datetime,
    @exclusion sysname,         -- pattern for fine comment line
    @tmp nvarchar(4000),
    @spring varchar(10),        -- space extensor to justify text
    @start varchar(16),         -- pattern of section start
    @end_deflare bit

declare @tkns table (
    id int identity(1,1) primary key,
    -- lno int,
    -- x1 int,
    -- x2 int,
    tkn nvarchar(4000),
    [val] nvarchar(4000)
    )

declare @exclusions table (
    lno int,
    pos int,    -- left position to exclude tag
    exclude bit -- 1=is included into exclude tokens
    )

declare @out table(lno int identity primary key,line nvarchar(4000))

-- =========================================================== initialization ==

select
    @as=isnull(@as,''''), -- @isection),
    @d=getdate(),
    @sep =''|'',
    @td=''%'',
    @dtype=126,
    @null='''',
    @replace_double_quotes=charindex(''|replace"|'',@opt),
    @out_out=charindex(''|out|'',@opt),
    @print=charindex(''|print|'',@opt),
    @noimacro=charindex(''|noimacro|'',@opt),
    @scissors=''-%8<%-'',
    @tkn=''%[%]%[a-z0-9_]%[%]%'',
    @exclusion=''%--[|]%[|]'',
    @start=''[%<]%[>%]:'',
    @spring=''/*@*/''

-- 120918\s.zaglio: nomixed automatically if...
if @as!='''' select @nomix=1

-- ================================================= split and store sections ==
if @section like ''%[''+@crlf+'']%''
    begin
    select @tpl=hashbytes(''md5'',left(@section,4000))

    -- if already compiled
    if exists(select top 1 null from #tpl_cpl where tpl=@tpl) goto ret

    select @hi_lno=isnull(max(lno)+1,0) from #tpl

    insert #tpl(line)
    select line -- sp__Script_template
    from dbo.fn__ntext_to_lines(@section,0)

    exec sp__script_template_compile @tpl,@start,@scissors

    goto ret
    end -- compile teplate

-- backward compatiblity
if @tpl_cpl_oid is null
    begin
    create table #tpl_cpl(tpl binary(20),section sysname,y1 int,y2 int)
    -- print hashbytes(''md5'',''compiled'')
    select @tpl=0xCB5185196AD3147D58C13C22B2A32292
    exec sp__script_template_compile @tpl,@start,@scissors
    end

select @bos=@isection+'':'',@lo_lno=min(lno),@hi_lno=max(lno) from #tpl

if @dbg=1
    select ''#tpl_cpl'' tbl,section,y1,y2,line
    from #tpl_cpl join #tpl t on t.lno between y1 and y2
    order by lno

if not exists(select top 1 null from #tpl) goto err_tpl

-- check for sections with null lines
if not object_id(''tempdb..#tpl_sec'') is null
    if exists(
        select top 1 null
        from #tpl_sec
        where line is null
        and (@isection is null or section=@isection)
        )
        begin
        if @isection is null
            select top 3 @isection=isnull(@isection+'','','''')+section
            from #tpl_sec
            where line is null
        goto err_sec
        end

-- collect line number to exclude
insert @exclusions(lno,pos,exclude)
select
    lno,
    case
    when patindex(@exclusion,rtrim(t.line))>2
    then len(rtrim(substring(t.line,1,patindex(@exclusion,rtrim(t.line))-1)))
    else len(t.line)
    end as pos,
    case
    when patindex(@exclusion,rtrim(t.line))>2
     and dbo.fn__str_between(
         substring(t.line,patindex(@exclusion,rtrim(t.line))+2,128)
         ,''|'',''|''
         ,default
         )
         in (
             select token
             from dbo.fn__str_table(@excludes,@sep)
             where token!=''''
            )
    then 1
    else 0
    end as exclude
from #tpl t

-- select * from #tpl t join @exclusions e on t.lno=e.lno

if @isection is null
    -- after last scissor
    select top 1 @y1=lno+1
    from #tpl
    where line like @scissors
    and lno!=@hi_lno -- skip last scissor on last line
    order by lno desc
else
    -- begin of section
    select top 1 @y1=lno+1
    from #tpl
    where ltrim(rtrim(line)) = @bos  -- 120913\s.zaglio: removed like
    order by lno

if @y1 is null and not @isection is null goto err_snf

select @y1=isnull(@y1,@lo_lno)

-- end of section (scissors or eof or [ssep]%[ssep]
if not @isection is null
    select top 1 @y2=lno-1
    from #tpl
    where 1=1
    and lno>=@y1
    and line like @scissors
    order by lno

-- last section end is last line
if @y2 is null select @y2=@hi_lno

-- ======================================================== second params chk ==

-- ===================================================================== body ==

-- extract section lines and mix with info from #tpl_sec or print missing data

-- ##################################
-- ##
-- ## mix
-- ## if #tpl_sec contain the section use it, otherwise
-- ## replace with original section from #tpl or give error or unk. section
-- ##
-- #############################################################################
if object_id(''tempdb..#tpl_sec'') is null
    insert @out(line)
    select
        left(t.line,excl.pos)
        as line
    from #tpl t
    left join @exclusions excl on t.lno=excl.lno
    where 1=1
    and t.lno between @y1 and @y2
    and not t.line collate database_default like @scissors
    and excl.exclude=0
    order by t.lno
else
    insert @out(line)
    select
        case
        when s.section is null
        then case
             when tts.section is null
             then left(t.line,excl_t.pos)
             else left(replace(left(t.line,excl_t.pos) collate database_default,
                               tts.section collate database_default,
                               tt.line collate database_default),
                       excl_tt.pos)
             end
        else replace(left(t.line,excl_t.pos) collate database_default,
                     s.section collate database_default,
                     s.line collate database_default)
        end
        as line
    from #tpl t
    left join @exclusions excl_t on t.lno=excl_t.lno
    left join (
        select s.lno*1000+pos lno,s.section,token as line
        from #tpl_sec s
        cross apply dbo.fn__str_table_fast(s.line,@crlf)
        -- where charindex(s.section,t.line)>0 not possible
        ) s
        -- #tpl_sec s
        on charindex(s.section,t.line)>0
    left join #tpl_cpl tts
        on charindex(tts.section,t.line)>0
    left join #tpl tt
        on tt.lno between tts.y1 and tts.y2
    left join @exclusions excl_tt on tt.lno=excl_tt.lno
    where 1=1
    and t.lno between @y1 and @y2
    and not t.line like @scissors collate database_default
    and isnull(excl_t.exclude,0)=0
    and isnull(excl_tt.exclude,0)=0
    order by t.lno,s.lno

if @@rowcount=0 goto err_mix

insert @tkns(tkn,val)
select
    token,
    convert(nvarchar(4000),
            case pos
            when 1 then @v1
            when 2 then @v2
            when 3 then @v3
            when 4 then @v4
            end
            )
from fn__str_table(@tokens,@sep)

if not object_id(''tempdb..#vars'') is null
    insert @tkns(tkn,val)
    select
        v.id,
        case
        when sql_variant_property(v.[value],''BaseType'')=''datetime''
        then convert(nvarchar(48),v.[value],@dtype)
        else convert(nvarchar(4000),coalesce(v.[value],@null))
        end
    from #vars v
    left join @tkns t on v.[id]=t.tkn collate database_default
    where t.tkn is null

-- add local macro
if @noimacro=0
    insert  @tkns(tkn,val)
        select m.id,m.value
        from (
            select ''%db%'' id,db_name() value union
            select ''%now%'',convert(nvarchar(48),getdate(),126) union
            select ''%dt%'',substring(convert(nvarchar(48),getdate(),126),3,14) union
            select ''%uid%'',system_user+''@''+host_name()
            ) m
        left join @tkns t on m.id=t.tkn
        where t.tkn is null

-- introduce here the reading of last indent, if @tab=0
if @tab=0
    select top 1 @tab=patindex(''%[^ ]%'',line)-1 -- is a len
    from @out
    order by lno desc

if @dbg=1
    begin
    select @section as [@section],@as as [@as],
           @x1 as [@x1],@y1 as [@y1],@x2 as [@x2],@y2 as [@y2],
           @tab as [@tab],@itab as [@itab]
    select ''tpl'' tpl,* from #tpl
    select ''@out'' [out],@isection as sec,* from @out order by lno
    select ''@tkns'' tkns,* from @tkns

    select ''#tpl_sec'' sec,* from #tpl_sec
    select ''@ex'' ex,* from @exclusions
    end -- dbg

declare cs cursor local forward_only for
    select lno,/*space(@itab)+*/line
    from @out
    order by lno

/*  TODO:   convert tokens in inside sections and/or allow
            that tokens with CRLF will be correctly expandend
            to allow a correct compare between composed #src
            and its compiled version
*/
-- replace tokens & "
open cs
while 1=1
    begin
    fetch next from cs into @i,@line
    if @@fetch_status!=0 break

    if @line is null goto err_nll

    select @oline=@line

    select
        @line=replace(@line,tkn,convert(nvarchar(4000),val))
    from @tkns t
    where charindex(t.tkn,@line)>0

    -- count "
    if @replace_double_quotes=1
        begin
        select @n=0,@i=charindex(''"'',@line,1)
        while @i>0
            select @n=@n+1,@i=charindex(''"'',@line,@i+1)
        if @n>0 and @n%2=0 select @line=replace(@line,''"'','''''''')
        end

    if @line is null goto err_nlr

    if charindex(@spring,@line)>0
    and (80-len(@line)+len(@spring))<=80
        select @line=replace(
                        @line,
                        @spring,
                        replicate('' '',80-len(@line)+len(@spring))
                        )

    if @print=1
        begin
        select @tmp=replace(@line,''%'',''%%'')
        raiserror(@tmp,10,1) with nowait
        end
    else
        update @out set line=@line where current of cs
    end -- while of cursor
close cs
deallocate cs

if not @isection is null
and @nomix=1
    insert #tpl_sec(section,line)
    select @as,line
    from @out
    order by lno
else
    begin
    if @print=0 -- already printed above
        begin
        if object_id(''tempdb..#src'') is null or @out_out=1
            begin
            if object_id(''tempdb..#out'') is null
                select line from @out order by lno
            else
                insert #out(line) select line from @out order by lno
            end -- out to #out
        else
            insert #src(line) select line from @out order by lno
        end -- out to #src
    end -- out

goto ret

-- =================================================================== errors ==
err_mix:    exec @ret=sp__err   ''fusion (y1:%d,y2:%d)'',
                                @proc,@p1=@y1,@p2=@y2
            goto ret
err_tpl:    exec @ret=sp__err   ''empty #tpl'',
                                @proc,@p1=@isection
            goto ret
err_cpl:    exec @ret=sp__err   ''undeclared #tpl_cpl'',
                                @proc
            goto ret
err_snf:    exec @ret=sp__err   ''section "%s" not found in #tpl'',
                                @proc,@p1=@isection
            goto ret
err_nlr:    exec @ret=sp__err   ''null line result in (%s)'',
                                @proc,@p1=@oline
            goto ret
err_nll:    exec @ret=sp__err   ''null input line (sec:%s)'',
                                @proc,@p1=@isection
            goto ret
err_sec:    exec @ret=sp__err   ''null line in #tpl_sec for (sec:%s)'',
                                @proc,@p1=@isection
            goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    help programmer to generate code from template;
    generally script a section of #tpl in #tpl_sec and
    finally MIX the specified or last section into console or #src if present.

Todo
    - check for duplicates on init

Notes
    since version 130713, #tpl and #tpl_cpl are duty and inner section of #tpl
    are not auto expanded (sorry) so you need to pre-expand into #tpl_sec
    create table #tpl(lno int identity primary key,line nvarchar(4000))
    create table #tpl_sec(lno int identity,section sysname,line nvarchar(4000))
    create table #tpl_cpl(tpl binary(20),section sysname,y1 int,y2 int)

Parameters
    #tpl        the source template
    #tpl_cpl    is the compiled version of #tpl and allows inner calls as for example
                between sp__script_group and sp__script
    #tpl_sec    (optional)the template section replacers
                if omitted, output directly to #src or #out
    #src        (optional) where store the compiled result
    #out        (optional) where store the compiled result if #src is not given
    @section    - templates to init #tpl
                - script @section of #tpl into #tpl_sec with @section as name
                (@section identify lines between %section%: and next --8<--8<-- scissor)
                @tokens are replaced with @v1,@v2,...
                If null, insert into #src the last section (tipical MAIN at end of code),
                mixing content from #tpl_sec and replacing words as %tpl_sec.section%
                with tpl_sec.line (multiple lines are allowed).
    @as         alternative @section name to use when section is scripted into #tpl_sec
                (this do not do any mix into #src/#out)
    @opt        options, see below
    @tokens     (optional) tokens, separated by | to replace with @v1..4
                if omitted, collects couples of %...% (see also #vars)
    @v1..4      values for tokens
    @tab        out the number of spaces before the splitter
                or the left spaces of last row of section;
                if valued, shift the section
    @excludes   tokens (sep. by |) of lines to exclude (line that end with "--|token|"
    #vars       (optional) macro replace (see sp__str_replace) (@tokens overwrite #vars)
                (macro %db%,%uid%,%dt%,%now% are pre-defined)
                create table #vars (id nvarchar(16),value sql_variant)
    @dbg        2 inner create #tpl to test insert

Notes
    1. if not all sections are covered by #tpl_sec, a list of sections is printed
    2. if all sections are covered, the sp merge #tpl and sec into #src or #out or print
    3. the indent of ##section## is applied to lines of #tpl_sec
    4. There are predefined macros:
        %db%    current db
        %now%   current datetime in current format (126)
    5. scissors are skipped (any line like "-%8<%-")
       -- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
    6. /*@*/    justify to right at col 80, the rest of text

Options
    replace"    replace " with double ''''
    noimacro    disable autodefinition of inner macros (%db%,%now%,...)
    print       print lines instead insert into #src or #out or #tpl_sec
    out         force output to #out instead of #src

Examples

    -- given this template...

    exec sp__script_template ''''
    -- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
    %header%:
        %detail%
    -- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
    %details%:
    line 1
    line 2
    -- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
    ''''

    -- ... or call the sp @as to out to #tpl_sec to be remixed

    exec sp__script_template ''''%details%'''',''''%detail%''''
    exec sp__script_template ''''%header%''''

    see sp__script_template_test, sp__script_group, sp__script_data

''

if not object_id(''tempdb..#tpl'') is null
and object_id(''tempdb..#tpl_sec'') is null
    begin
    exec sp__select_astext ''
        select line as sections from #tpl where rtrim(line) like ''''%:''''
        ''
    end

select @ret=-1

-- ===================================================================== exit ==
ret:
if @dbg>2 exec sp__elapsed @d,@proc
return @ret

end -- proc sp__script_template'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_template:

-- ================================================= sp__script_template_compile
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_template_compile',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130728
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_template_compile') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_template_compile') with nowait
        goto skip_sp__script_template_compile
        end
    if @ver>130728
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_template_compile') with nowait
        goto skip_sp__script_template_compile
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_template_compile') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_template_compile'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_template_compile]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130728\s.zaglio: compile #tpl into #tpl_clp
*/
create proc sp__script_template_compile
    @tpl binary(20) = null,
    @start varchar(16),
    @scissors nvarchar(32),
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @bos sysname,               -- begin of section
    @lo_lno int,@hi_lno int     -- low and high line number

-- =========================================================== initialization ==
-- ======================================================== second params chk ==
if @tpl is null goto help
-- =============================================================== #tbls init ==
-- ===================================================================== body ==

-- compile #tpl into #tpl_cpl
while 1=1
    begin
    -- search for session begin
    select top 1 @lo_lno=lno+1,@bos=ltrim(rtrim(line))
    from #tpl
    where ltrim(rtrim(line)) like @start
    and lno>@hi_lno
    order by lno
    if @@rowcount=0 break
    -- search for session end
    select top 1 @hi_lno=lno-1
    from #tpl
    where lno>=@lo_lno
    and (ltrim(rtrim(line)) like @start
    or ltrim(rtrim(line)) like @scissors)
    order by lno
    if @@rowcount=0 select @hi_lno=max(lno) from #tpl
    select @bos=ltrim(rtrim(left(@bos,charindex('':'',@bos)-1)))
    insert #tpl_cpl select @tpl,@bos,@lo_lno,@hi_lno
    end


-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    compile #tpl into #tpl_clp

Parameters
    [param]     [desc]
    #tpl        template source
    #tpl_clp    table with compiled data
    @opt        options (not used)
    @dbg        not used
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_template_compile'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_template_compile:

-- ==================================================== sp__script_template_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_template_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130802
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_template_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_template_test') with nowait
        goto skip_sp__script_template_test
        end
    if @ver>130802
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_template_test') with nowait
        goto skip_sp__script_template_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_template_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_template_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_template_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130802,130709\s.zaglio: test sp__script_template
*/
CREATE proc sp__script_template_test
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @chk binary(16),@ochk binary(16),
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
        |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @ochk=0x978bf8a6061e18864a27b2c7594f901b,
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
-- if  @run=0 goto help

-- =============================================================== #tbls init ==
create table #src(lno int identity primary key,line nvarchar(4000))
create table #tpl(lno int identity primary key,line nvarchar(4000))
create table #tpl_sec(lno int identity,section sysname,line nvarchar(4000))
insert #tpl(line) select line from dbo.fn__ntext_to_lines(''
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%header%:
    %detail%
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%details%:
line %n%
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
'',0)
-- ===================================================================== body ==

exec sp__script_template ''%details%'',''%detail%'',@tokens=''%n%'',@v1=1
exec sp__script_template ''%details%'',''%detail%'',@tokens=''%n%'',@v1=2
exec sp__script_template ''%header%''

-- sp__script_template_test
exec sp__md5 @chk out

exec sp__prints''template source''
exec sp__print_table ''#tpl''
exec sp__prints''template rendered''
exec sp__print_table ''#src''
exec sp__prints''results''
exec sp__printf ''template rendering original:%d'',@ochk
exec sp__printf ''template rendering checksum:%d'',@chk
-- sp__script_template_test
if @chk!=@ochk raiserror(''test failed'',16,1)
else exec sp__printf ''test passed''

-- ================================================================== dispose ==
dispose:
drop table #tpl
drop table #tpl_sec
drop table #src

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test sp__script_template functionalities

TODO:
    * simple test to console
    * mix a template that contain other template section to #src
      the section must contain another section (recursion test)
    * mix template and sections into #out
    * test tokens and #tpl_sec sections with CRLF

Parameters
    [param]     [desc]
    @opt        options
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_template_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_template_test:

-- ======================================================== sp__script_templates
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_templates',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130901.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_templates') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_templates') with nowait
        goto skip_sp__script_templates
        end
    if @ver>130901.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_templates') with nowait
        goto skip_sp__script_templates
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_templates') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_templates'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_templates]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130901.1000\s.zaglio:added begin/end params to grp setup
    v:130802.1804,130730,130725,130724\s.zaglio:collection of scripts
*/
CREATE proc sp__script_templates
    @opt sysname = null,
    @dbg int=0
as
begin try
set nocount on

declare @proc sysname, @ret int
select @ret=0

if @opt is null or @opt='''' goto help

select @opt=dbo.fn__str_quote(@opt,''|'')

-- =================================================================== script ==

if charindex(''|script|'',@opt)>0 exec @ret=sp__script_template ''
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%declarations%:
declare
    @ver decimal(10,4),@aut sysname,@db sysname,
    @emsg nvarchar(2048),@esev int,@ests int
select @db=db_name()
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%obj_ver_chk%:
select @ver=null,@aut=null
exec sp_executesql N''''
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    '''',N''''@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out'''',
    @obj=''''%obj%'''',@typ=''''rv'''',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=%ver%
        begin
        if @aut!=''''%aut%''''
            raiserror(''''local "%s.%s" with same version but different author'''',
                      16,1,@db,''''%obj%'''') with nowait
        raiserror(''''skipped "%s.%s" because local is the same'''',
                  10,1,@db,''''%obj%'''') with nowait
        goto skip_%obj%
        end
    if @ver>%ver%
        begin
        raiserror(''''skipped "%s.%s" because local is more recent'''',
                  10,1,@db,''''%obj%'''') with nowait
        goto skip_%obj%
        end
    end

raiserror(''''re-creating "%s.%s"'''',10,1,@db,''''%obj%'''') with nowait

%drop%

begin try
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%skip_obj%:
end try
begin catch
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_%obj%:
''

-- ==================================================================== group ==

if charindex(''|group|'',@opt)>0 exec sp__script_template ''
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%scr_header%:
/*******************************************************
** generated in the:%dt% by %uid% from %db%
** latest objs to %latests_objs%
*******************************************************/
set nocount on
declare
    @db sysname,@svr sysname,
    @emsg nvarchar(2048),@esev int,@ests int

select @db=db_name(), @svr=@@servername

if @svr=''''%svr%'''' and @db=''''%db_util%''''                           --|utility|
    begin                                                         --|utility|
    raiserror(''''Cannot execute from db where originated.'''',       --|utility|
              11,1) with nowait                                   --|utility|
    goto end_of_script                                            --|utility|
    end                                                           --|utility|
                                                                  --|utility|
-- check local our target application name                        --|other|
if %config%(%app_name%)=''''%app_name%'''' goto end_of_script         --|other|
                                                                  --|other|
-- check db version
if (select cmptlevel
    from master..sysdatabases
    where [name]=@db
    )<90
    begin
    raiserror(''''DB compatibility level must be at least 90 (MSSql2k5)'''',
              10,1) with nowait
    raiserror(''''Please run "exec sp_dbcmptlevel ''''''''%s'''''''', 90"'''',
              10,1,@db) with nowait
    raiserror('''''''',11,1) with nowait
    goto end_of_script
    end


if not object_id(''''sp__script_store'''') is null                    --|other|
    exec sp__script_store @opt=''''moff''''                           --|other|

-- for versioning
declare
    @aut sysname,                   -- store author
    @ver decimal(14,4),             -- used to store new obj version
    @msg nvarchar(128),             -- generic message string
    @repeat int                     -- number of repeat of script
                                    -- if error of unknow obj, script is
                                    -- repeated bacause dependencies

if object_id(''''tempdb..#script_results'''') is null
    create table #script_results(
        id int identity,
        dt datetime default(getdate()) not null,
        who sysname default system_user+''''@''''+isnull(host_name(),''''???''''),
        grp sysname not null,
        rep int null,
        number int null,
        message nvarchar(2048) not null,
        severity int not null,
        state int null,
        line int null,
        [procedure] sysname null
        )

if object_id(''''tempdb..#script_catch'''') is null
    exec(''''
        %script_catch_implementation%
    '''')

select
    @repeat=1

begin_of_script:
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%scr_footer%:

dispose:
/*  to manage dependencies we run eventually the script 2 or 3 times */
if exists(
    select top 1 null
    from #script_results
    where number in (208,207)  -- invalid object, invalid column name
    and rep=@repeat
    -- a difference between x32 and x64 of mssql 2k5/8
    and not message like ''''%''''''''cpu_ticks_in_ms''''''''%''''
    )
    begin
    select @repeat=@repeat+1
    select @msg=replicate(''''#'''',80)+'''' REPEAT ''''+cast(@repeat as char)
    raiserror(@msg,10,1)
    if @repeat<3
        goto begin_of_script
    else
        begin
        select db_name() db,* from #script_results
        raiserror(''''script repeats failure'''',16,1)
        end
    end

exec %grp_setup% @opt=''''run|end''''                                   --|setup|

-- select * from #script_results
-- drop table #script_results
exec sp__context_info @opt=''''mdef''''                               --|other|

end_of_script:

-- finished:%dt%
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%script_catch_definition%:
create proc #script_catch @grp sysname,@repeat int
as
begin
insert #script_results(dt,grp,rep,number,message,severity,state,line,[procedure])
select getdate(),@grp,@repeat,
       error_number(),isnull(error_message(),''''n/s''''),
       error_severity(),error_state(),
       error_line(),error_procedure()
end
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%disable_tracer%:

-- ##########################
-- ##
-- ## disable trace db;
-- ## hope nobody work on important things while upgrade
-- ##
-- ########################################################
if not object_id(''''sp__script_trace_db'''') is null
    exec sp__script_trace_db ''''uninstall''''

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%enable_tracer%:

-- ##########################
-- ##
-- ## re-install/upgrade script tracer
-- ## upgrade LOG_DDL if necessary
-- ##
-- ########################################################
exec sp__script_trace_db ''''install''''
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%obj_core_chk%:
select @ver=dbo.fn__script_sign(''''%obj%'''',1)
if not @ver is null
and @ver=%ver%
    begin
    raiserror(''''skipped "%s.%s" because is the same'''',
              10,1,@db,''''%obj%'''') with nowait
    goto skip_%obj%
    end

raiserror(''''re-creating "%s.%s"'''',10,1,@db,''''%obj%'''') with nowait

%drop%

begin try
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%skip_obj%:
exec %grp_setup% @opt=''''run|begin''''                                  --|setup|

end try
begin catch
exec #script_catch ''''%grp%'''',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_%obj%:
''

if charindex(''|move|'',@opt)>0 exec sp__script_template ''
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%header%:
/*                                                                  --|dbg|
declare                                                             --|dbg|
    @trgs nvarchar(4000),@crlf nvarchar(2),@n int,@m int,           --|dbg|
    @proc sysname,@err int,@ret int,@d datetime,                    --|dbg|
    @ms_key int,@ms_ins int,@ms_del int, @ms_commit int,@ms_alt int,--|dbg|
    @log_id int,@top int                                            --|dbg|
select @crlf=crlf from fn__sym()                                    --|dbg|
*/                                                                  --|dbg|
declare @nm int

--=============================================================================
--== do some test to ensure that condition is not wrong
--=============================================================================
select @d=getdate()

begin try

select
    @n=count(*),
    @m=sum(case when %where% then 1 else 0 end)
from %main% (nolock)

if @n=@m
    begin
    raiserror("wrong condition try move all data",16,1)
    goto ret
    end

select @trgs=null                                           --|trgs|
select                                                      --|trgs|
    @trgs=isnull(@trgs+@crlf,"")                            --|trgs|
         +"alter table %dst% enable trigger "               --|trgs|
         +quotename(name)                                   --|trgs|
from %dst_db%.sys.triggers                                  --|trgs|
where parent_id=object_id("%dst%")                          --|trgs|
and is_disabled=0                                           --|trgs|
                                                            --|trgs|
select @idxs=null                                           --|idxs|
select                                                      --|idxs|
    @idxs=isnull(@idxs+@crlf,"")                            --|idxs|
         +"alter index "+quotename(name)                    --|idxs|
         +" on %dst% disable"                               --|idxs|
from %dst_db%.sys.indexes                                   --|idxs|
where object_id=object_id("%dst%")                          --|idxs|
and is_disabled=0                                           --|idxs|
and is_unique=0                                             --|idxs|
and [type]!=1                                               --|idxs|
                                                            --|idxs|
select                                                      --|idxs|
    @trgs=isnull(@trgs+@crlf,"")                            --|idxs|
         +"alter index "+quotename(name)                    --|idxs|
         +" on %dst% rebuild"                               --|idxs|
from %dst_db%.sys.indexes                                   --|idxs|
where object_id=object_id("%dst%")                          --|idxs|
and is_disabled=0                                           --|idxs|
and is_unique=0                                             --|idxs|
and [type]!=1                                               --|idxs|
                                                            --|idxs|
select                                                      --|trgs|
    @trgs=isnull(@trgs+@crlf,"")                            --|trgs|
         +"alter table %src% enable trigger "               --|trgs|
         +quotename(name)                                   --|trgs|
from sys.triggers                                           --|trgs|
where parent_id=object_id("%src%")                          --|trgs|
and is_disabled=0                                           --|trgs|
                                                            --|trgs|
-- for delete                                               --|trgs|
alter table %src% disable trigger all                       --|trgs|
-- for insert                                               --|trgs|
alter table %dst% disable trigger all                       --|trgs|
-- disable dst indexes                                      --|idxs|
exec(@idxs)                                                 --|idxs|
                                                            --|idxs|
if @dbg=1 exec sp__printf "%s",@idxs                        --|idxs|
                                                            --|idxs|
select @ms_alt=datediff(ms,@d,getdate()),@d=getdate()

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%begin%:
begin tran  --------------------------------------------------- begin tran ----

-- select keys to move
-- TODO: collect keys for each table to avoid inverse order of deletetion
select %top% %pkey%
into %keys%
from %main%
where %where%
%orderby%
select @nm=@@rowcount
if @nm=0
    begin
    exec sp__printf "-- no rows to move for main table %src%"
    goto skip_move
    end

select @ms_key=datediff(ms,@d,getdate()),@d=getdate()

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%end%:
commit  ------------------------------------------------------ commit tran ----
select @ms_commit=datediff(ms,@d,getdate()),@d=getdate()

%log_commit%

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%body_group%:

-- ##########################
-- ##
-- ## %src%
-- ##
-- ########################################################

insert %dst%(
    %dst_flds%
    )
select
    %tbl_flds%
from %main% %main_alias%
join %keys% tmp
on %on_pkeys%
join %src% %alias%                      --|main|
on %join_on%                            --|main|

select @ms_ins=datediff(ms,@d,getdate()),@d=getdate()

-- delete from live                     --|move|
delete %alias%                          --|move|
from %main% %main_alias%                --|move|
join %keys% tmp                         --|move|
on %on_pkeys%                           --|move|
join %src% %alias%                      --|main|move|
on %join_on%                            --|main|move|

select @ms_del=datediff(ms,@d,getdate()),@d=getdate()

%log_times%
-------------------------------------------------------------------------------

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%body_single_table%:

-- ##########################
-- ##
-- ## %src%
-- ##
-- ########################################################

-- select keys to move
-- TODO: collect keys for each table to avoid inverse order of deletetion
select %top% %pkey%
into %keys%
from %main%
where %where%
%orderby%
select @nm=@@rowcount
if @nm=0
    begin
    exec sp__printf "-- no rows to move for %src%"
    goto skip_%lbl%
    end

select @ms_key=datediff(ms,@d,getdate()),@d=getdate()

begin tran  --------------------------------------------------- begin tran ----

insert %dst%(
    %dst_flds%
    )
select
    %tbl_flds%
from %src% %alias%
join %keys% tmp
on %on_pkeys%

select @ms_ins=datediff(ms,@d,getdate()),@d=getdate()

-- delete from live                                             --|move|
delete %alias%
from %src% %alias%
join %keys% tmp
on %on_pkeys%

select @ms_del=datediff(ms,@d,getdate()),@d=getdate()
commit  ------------------------------------------------------ commit tran ----
select @ms_commit=datediff(ms,@d,getdate()),@d=getdate()

%log_times%

skip_%lbl%:

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%footer%:
-- restore triggers                                             --|trgs|
exec(@trgs)                                                     --|trgs|
if @dbg=1 exec sp__printf "%s",@trgs                            --|trgs|

skip_move:
if @@trancount>0 rollback
end try ---------------------------------------------------------- end try ----
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%catch%:

begin catch
select @err=@@error
exec @ret=sp__err @cod=@proc,@opt="ex|warn"
if @@trancount>0 rollback

end catch
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%log_times_section%:
insert %log%(dt,tbl,n,ms_key,ms_alt,ms_ins,ms_del,ms_commit)        --|nfo|
select @dt_log,                                                     --|nfo|
       ''''%src%'''',@nm,@ms_key,@ms_alt,@ms_ins,@ms_del,@ms_commit    --|nfo|

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%log_commit_section%:
insert %log%(dt,tbl,n,ms_key,ms_alt,ms_ins,ms_del,ms_commit)        --|nfo|
select @dt_log,''''*'''',0,0,0,0,0,@ms_commit                           --|nfo|

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
''

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
select @proc=object_name(@@procid)
exec sp__usage @proc,''
Scope
    common templates for sp__script and sp__script_group

Parameters
    [param]     [desc]
    @opt        options
                script  templates for sp__script
                group   templates for sp__script_group
                move    templates for sp__script_move
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_templates'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_templates:

-- =========================================================== sp__script_tohtml
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_tohtml',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110621
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_tohtml') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_tohtml') with nowait
        goto skip_sp__script_tohtml
        end
    if @ver>110621
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_tohtml') with nowait
        goto skip_sp__script_tohtml
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_tohtml') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_tohtml'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_tohtml]

begin try
exec dbo.sp_executesql @statement = N'/*  Keep this due MS compatibility
    l:see LICENSE file
    g:utility
    v:110621\s.zaglio: changed syntaxhl. for compatibility with ffox4
    v:110614\s.zaglio: adapted to new script group
    v:100612\s.zaglio: added margins
    r:100405\s.zaglio: todo: omit headers is exists; manage a mix
    v:100404\s.zaglio: htmlize the code in #src
    t:sp__script_tohtml ''sp__script_tohtml''
*/
CREATE proc [dbo].[sp__script_tohtml]
    @obj sysname=null,
    @dbg smallint=0                      -- enable print of debug info
as
begin
set nocount on
if @dbg>=@@nestlevel exec sp__printf ''level of debugging:%d'',@@nestlevel
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0
declare @crlf nvarchar(2),@txt nvarchar(4000)
select @crlf=crlf from fn__sym()

if @obj is null and object_id(''tempdb..#src'') is null goto help

if object_id(''tempdb..#src'') is null
    begin
    create table #src(lno int identity,line nvarchar(4000))
    exec sp__script @obj
    end

create table #tmp (lno int identity(10,10),line nvarchar(4000))
select @txt=''<link type="text/css" rel="stylesheet"
    href="css/sh/SyntaxHighlighter.css"></link>
<script language="javascript" src="js/sh/shCore.js"></script>
<script language="javascript" src="js/sh/shBrushTSql.js"></script>
</head>
<body marginwidth="4" marginheight="4" topmargin="4" leftmargin="4" rightmargin="4" bottommargin="4"
    onload="
        dp.SyntaxHighlighter.ClipboardSwf = ''''js/sh/clipboard.swf'''';
        dp.SyntaxHighlighter.HighlightAll(''''code'''');
        ">
<pre name="code" class="tsql">''
insert #tmp select token from dbo.fn__str_table(@txt,@crlf)
insert #tmp select line from #src order by lno
insert #tmp select ''</pre>''
--insert #tmp select ''</pre>''
insert #tmp select ''</body></html>''

truncate table #src
insert #src select line from #tmp order by lno

drop table #tmp

if not @obj is null select line from #src order by lno
goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    encapsulate #src code into html

Parameters
    #src    source code
    @obj    direct name of obj
''
select @ret=-1

ret:
return @ret
end -- sp__Script_tohtml'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_tohtml:

-- ========================================================= sp__script_trace_db
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_trace_db',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131201.0900
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_trace_db') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_trace_db') with nowait
        goto skip_sp__script_trace_db
        end
    if @ver>131201.0900
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_trace_db') with nowait
        goto skip_sp__script_trace_db
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_trace_db') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_trace_db'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_trace_db]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,script
    v:131201.0900\s.zaglio: adapted to change of tids.svr->tids.srv
    v:131125\s.zaglio: moved manage of log_ddl into sp__utility_setup
    v:131002\s.zaglio: moved code from trigger db to sp__script_store
    v:120924\s.zaglio: added upgrade of log_ddl from 1477379425 to 45011731
    v:120907\s.zaglio: removed skip of objs %__% from tr
    v:120824\s.zaglio: bug when zero condition near drop of fn__script_trace
    v:120517\s.zaglio: bug about create of fn__script_trace
    v:120516\s.zaglio: adapted to new fn__script_sysobjs
    v:120512.1522\s.zaglio: managed new DML and CTRL events
    v:120213\s.zaglio: adapted to new fn__script_events
    v:120208\s.zaglio: removed identity in log_ddl and done fn__script_trace
    r:120207\s.zaglio: working
    r:120206\s.zaglio: modified fn__script_trace and log_ddl
    d:120103\s.zaglio: sp__util_ddl
    v:111205\s.zaglio: bug given by change of sp__script_compile beahviour
    v:110830\s.zaglio: adapted to new fn__buildin
    v:110629\s.zaglio: now trigger exclude %__%
    v:110628.1644\s.zaglio: managed not presence of sp__script_store
    v:110624\s.zaglio: added remove
    d:110623\s.zaglio: sp__script_trace
    v:110510\s.zaglio: added trace view filter by id and obj name
    v:110415.1712\s.zaglio: added test of cmptlvl and fn__strace generation
    v:110315\s.zaglio: reduced table size and introduced some checks and exclus.
    v:110313\s.zaglio: done vertioning of all db objects
    r:110312\s.zaglio: 1st draft
    t:sp__script_trace_db ''install'' -- sp__Script_history
    t:sp__script_trace_db ''uninstall''
    t:sp__script_trace_db ''remove''
    t:select * from log_ddl order by dt desc
*/
CREATE proc sp__script_trace_db
    @opt nvarchar(4000)=null,
    @dbg bit=null
as
begin
set nocount on
/*
select t.name,*
from sys.trigger_events e
join sys.triggers t
on t.object_id=e.object_id
select * from sys.sql_modules where object_id=142675606
select * from sys.events
select * from sys.trigger_events e
*/
declare @proc sysname,@ret int
select  @proc=object_name(@@procid),@ret=0,@dbg=isnull(@dbg,0),
        @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

declare
    @sql nvarchar(max),@tr_name sysname,@events nvarchar(max),
    @tab2 sysname,@crlf nvarchar(2),@srv_id int,@db_id int,
    @tmp nvarchar(4000),@fdb smallint,
    @tsrv tinyint,@tdb tinyint,@thost tinyint,
    @tapp tinyint,@tsql tinyint,@tobj tinyint,
    @tev tinyint,
    @cond nvarchar(32),@db sysname,@srv sysname,
    @srv_hash int,@db_hash int,@udt datetime,
    @counter_id int,
    @cmd_install bit,@cmd_uninstall bit,
    @cmd_remove bit,@upgrade bit,
    @ftype smallint

select
    @counter_id =-2147483648,
    @upgrade    =0,
    @udt=   getutcdate(),
    @srv=   dbo.fn__servername(null),
    @db=    db_name(),
    @srv_hash=dbo.fn__crc32(@srv),
    @db_hash=dbo.fn__crc32(@db),
    @cmd_remove=charindex(''|remove|'',@opt),
    @cmd_install=charindex(''|install|'',@opt),
    @cmd_uninstall=charindex(''|uninstall|'',@opt),
    @srv_id=@counter_id+1,
    @db_id= @counter_id+2,
    @tr_name=replace(@proc,''sp_'',''tr_''),
    @crlf=crlf,@tab2=''    ''
from
    fn__sym()


-- prevent errors due change to tids,flags
if (@cmd_install=@cmd_uninstall and @cmd_remove=0) goto help

if @dbg=0 and exists(select null from sys.triggers t where t.name=@tr_name)
    begin
    exec(''drop trigger [''+@tr_name+''] on database'')
    if @@error!=0 goto err_rem
    else exec sp__printf ''DB trace trigger %s removed'',@tr_name
    end

if @cmd_uninstall=1 goto ret

if @cmd_remove=1
    begin
    drop table LOG_DDL
    goto ret
    end

exec sp_executesql N''
select
    @tsrv=  tids.srv,
    @tdb=   tids.db,
    @thost= tids.host,
    @tapp=  tids.app,
    @tsql=  tids.code,
    @tobj=  tids.obj,
    @tev=   tids.ev,
    @fdb=   flags.db,
    @ftype= flags.[type]
from
    tids,flags
'',N''
    @tsrv tinyint out,@tdb tinyint out,@thost tinyint out,
    @tapp tinyint out,@tsql tinyint out,@tobj tinyint out,
    @tev tinyint out,@fdb smallint out,@ftype smallint out
'',
    @tsrv=@tsrv out,@tdb=@tdb out,@thost=@thost out,
    @tapp=@tapp out,@tsql=@tsql out,@tobj=@tobj out,
    @tev=@tev out,@fdb=@fdb out,@ftype=@ftype out

if @opt is null or (@cmd_install=@cmd_uninstall and @cmd_remove=0)
    goto help

create table #blob(id int identity,blob ntext)
create table #src(lno int identity,line nvarchar(4000))
create table #vars (id nvarchar(16),value sql_variant)

insert #vars select ''%buildin%'',dbo.fn__script_buildin(getdate(),1,@proc,''autogeneration'')
insert #vars select ''%proc%'',   @proc
insert #vars select ''%tr_name%'',@tr_name
insert #vars select ''%tsrv%'',   @tsrv
insert #vars select ''%tdb%'',    @tdb
insert #vars select ''%thost%'',  @thost
insert #vars select ''%tapp%'',   @tapp
insert #vars select ''%tsql%'',   @tsql
insert #vars select ''%tobj%'',   @tobj

-- ================================================================= db trace ==

db_trace:

if (select cmptlevel
    from master..sysdatabases
    where [name]=@db
    )<90
    goto err_cml

-- ============================================================ upgrade table ==

    exec sp__utility_setup @opt=''run|log_ddl''

-- =============================================== re-create reading function ==
    -- drop function fn__script_trace
    if  object_id(''fn__script_trace'') is null or
        exists(
        select null
        from dbo.fn__script_info(''fn__script_trace'',''v'',0)
        where val1!=''131006.1100''
        )
        begin
        if not object_id(''fn__script_trace'') is null
            exec(''drop function fn__script_trace'')
        exec sp__printf ''-- creating fn__script_trace''
        exec(''/*  leave this
    l:see LICENSE file
    v:131006.1100\sp__script_trace: generated automatically
    t:select * from fn__script_trace(null,null,null) order by dt desc
*/
create function fn__script_trace(
    @obj sysname,
    @p2 bit,@p3 bit
    )
returns table
as
return
select
    l.id,
    l.srv srv_id,
    o.rid db_id,
    o.skey as obj,
    case l.tid
    when tids.code then case
        when l.flags & flg.ver=flg.ver then ''''ver''''
        else ''''rel''''
        end -- tsql
    else cast(l.flags as nvarchar)
    end as flags,
    u.skey as usr,
    substring(l.txt,1,4000) as code,
    l.ev,
    l.rel,
    l.dt
from tids,flags flg,log_ddl l with (readpast)
join log_ddl o with (readpast)
on o.tid=(select obj from tids) and o.id=l.rid
join log_ddl u with (readpast)
on u.tid=(select usr from tids) and u.id=l.pid
where l.tid=tids.code
and (@obj is null or
     l.rid=(select id
            from log_ddl
            where tid=(select obj from tids)
            and [key]=dbo.fn__crc32(@obj)
            and skey=@obj
            )
     )
-- end fn__script_trace
''
    )
    end

-- ============================================================= trigger code ==
insert #blob(blob)
select ''/*  leave this
    g:utility,trace
    v:%buildin%
    d:110628\s.zaglio:tr__script_trace
*/''

insert #blob(blob)
select ''create trigger %tr_name%
on database
for

''

-- =================================================================== events ==

select identity(int,1,1) id,@tab2+cod ev
into #events
-- select *
from fn__script_sysobjs(@tev) ev
join dbo.fn__str_table(@opt,''|'') op
on op.token!='''' and ev.cod like ''%''+op.token+''%''
where ev.flags&@fdb=@fdb
and ev.flags&@ftype=0

if @@rowcount=0
    insert #events(ev)
    select @tab2+cod ev
    -- select *
    from fn__script_sysobjs(@tev) ev
    where ev.flags&@fdb=@fdb
    and ev.flags&@ftype=0

update #events set ev=ev+'',''
where id!=(select max(id) from #events)

insert #blob(blob) select ev from #events
if @@rowcount=0 goto err_ev

insert #blob(blob)
select ''as
begin
set nocount on
declare
    @et sysname,@obj sysname,@app nvarchar(256),
    @sql nvarchar(max)

select
    @app=left(app_name(),256)

if left(@app,8)=''''SQLAgent'''' return

select @et=EVENTDATA().value(''''(/EVENT_INSTANCE/EventType)[1]'''',''''nvarchar(256)''''),
       @obj=EVENTDATA().value(''''(/EVENT_INSTANCE/ObjectName)[1]'''',''''nvarchar(256)''''),
       @sql=EVENTDATA().value(''''(/EVENT_INSTANCE/TSQLCommand)[1]'''',''''nvarchar(max)'''')

if object_id(''''sp__Script_store'''') is null goto err_prc

exec sp__script_store @et,@obj,@sql
goto ret

-- =================================================================== errors ==
err_prc:    print ''''%tr_name%: WARNING sp__script_store not found to store ''''+@obj

ret:
end -- trigger %tr_name%
''
-- split blob into lines
exec sp__write_ntext_to_lines @crlf=0

-- replace macros
exec sp__str_replace ''#src'',''#vars''

/*
<EVENT_INSTANCE>
    <EventType>type</EventType>
    <PostTime>date-time</PostTime>
    <SPID>spid</SPID>
    <ServerName>name</ServerName>
    <LoginName>name</LoginName>
    <UserName>name</UserName>
    <DatabaseName>name</DatabaseName>
    <SchemaName>name</SchemaName>
    <ObjectName>name</ObjectName>
    <ObjectType>type</ObjectType>
    <TSQLCommand>command</TSQLCommand>
</EVENT_INSTANCE>
*/

if @dbg=1 exec sp__printsql ''#src''
else
    begin
    exec @ret=sp__script_compile @opt=''noalter''
    if @ret!=0 exec sp__printsql ''#src''
    else exec sp__printf ''DB trace trigger %s installed'',@tr_name
    end

goto ret

-- =================================================================== errors ==
err_cml:    exec @ret=sp__err ''compatibility level to low; Use:
        declare @db sysname select @db=db_name()
        EXEC sp_dbcmptlevel @db, 90'',@proc goto ret
err_rem:    exec @ret=sp__err ''trigget not removed'',@proc goto ret
err_ev:     exec @ret=sp__err ''no events to attach'',@proc goto ret
-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    install/upgrade/uninstall DDL trigger.
    (Data Definition Language)

Notes
    Create the fn__script_trace.
    The DB trigger call the "sp__script_store"
    and this run a job that call the "sp__script_sync".
    See also sp__script_history.

Parameters
    @opt    options
            install     install db trigger
            uninstall   uninstall db trigger
            event|...   limit

Examples
    sp__script_trace_db "install"

-- List of events
''
exec sp__select_astext ''
    select
        ev.cod as [*event name*],
        case
        when ev.flags & flags.srv = flags.srv
        then ''''srv''''
        when ev.flags & flags.db = flags.db
        then ''''db''''
        end as [*type*]
    from flags,fn__script_sysobjs((select ev from tids)) ev
    where ev.flags&flags.db=flags.db
    and ev.flags&flags.[type]=0
    order by 1
    '',@header=1

help_trace_view:
if not object_id(''fn__script_trace'') is null
and not object_id(''log_ddl'') is null
    begin
    select @sql=''
    select *
    from fn__script_trace(%obj%,default,default)
    where %cond%
    order by dt desc,id desc
    ''
    if @db is null select @db=''default'',@cond=''1=1''
    else
        if isnumeric(@db)=1
            select @cond=''id=''+@db,@db=''default''
        else
            select @db=dbo.fn__str_quote(@db,''''''''),@cond=''1=1''

    exec sp__str_replace @sql out,''%obj%|%cond%'',@db,@cond
    exec(@sql)
    end
else
    exec sp__printf ''\n*** fn__script_trace absent; will be installed from this sp ***''

ret:
return @ret
end -- sp__script_trace_db'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_trace_db:

-- ========================================================= sp__script_transfer
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_transfer',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121010
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_transfer') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_transfer') with nowait
        goto skip_sp__script_transfer
        end
    if @ver>121010
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_transfer') with nowait
        goto skip_sp__script_transfer
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_transfer') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_transfer'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_transfer]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:move, script, remote, server
    r:121010\s.zaglio: short comment
    t:
        sp__script_transfer
            ''test'',
            ''continent|country|state|province|city|place|civic|
             palces|civics'',@opt=''sqlite''
            ,@dbg=2
*/
CREATE proc sp__script_transfer
    @dst sysname = null,
    @objs nvarchar(4000) = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    @i int,@n int,                      -- index, counter
    @sql nvarchar(max),                 -- dynamic sql
    -- options
    @nodata bit,
    @obj sysname,@typ sysname,
    @drop sysname,
    @crlf nvarchar(4),
    @end_declare bit

create table #src(lno int identity, line nvarchar(4000))
-- =========================================================== initialization ==
select
    @nodata=charindex(''|nodata|'',@opt),@opt=replace(@opt,''|nodata|'',''|''),
    @crlf=crlf,
    @end_declare=1
from fn__sym()

-- ======================================================== second params chk ==
if @opt=''||'' goto help

-- ===================================================================== body ==

declare cs cursor local for
    select o.token,so.typ,so.[drop]
    from fn__str_params(@objs,''|'',default) o
    join fn__sysobjects(default,default,default) so
    on o.token=so.obj -- select top 1 * from  sys.objects
open cs
while 1=1
    begin
    fetch next from cs into @obj,@typ,@drop
    if @@fetch_status!=0 break

    exec sp__prints @obj

    truncate table #src
    exec sp__Script @obj,@opt=@opt
    select @sql=''drop ''+@drop+'' if exists ''+@obj+'';''
    select @sql=isnull(@sql+@crlf,'''')+line from #src order by lno
    select @sql=''exec(''+@crlf+
                ''''''''+replace(@sql,'''''''','''''''''''')+''''''''+@crlf+
                '') at ''+quotename(@dst)

    if @dbg>0 exec sp__printsql @sql
    if @dbg<2 exec(@sql)

    if @nodata=0 and @typ=''U''
        begin
        select @sql=dbo.fn__sql_trim(''
            insert openquery(''+@dst+'',
                             ''''select * from ''+@obj+''''''
                            )
            select * from ''+@obj)
        if @dbg>0 exec sp__printsql @sql
        if @dbg<2 exec(@sql)
        end -- @nodata

    end -- cursor cs
close cs
deallocate cs

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
/*
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_me1:    select @e_msg=''write here msg''                              goto err
err_me2:    select @e_msg=''write this %s'',@e_p1=@var                    goto err
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    script objects one by one and execute on remote @dst server;
    useful for ODBC or non MSSQL engines

Parameters
    @dst    linked server
    @objs   list of objects separated by |
    @opt    options
            nodata  do not transfer data for tables
            ...     pass the option to sp__script
    @dbg    1 print sql and execute
            2 print sql but not execute

Examples
    [example]

-- list of remove servers --
''
exec sp__select_astext ''
    select name,data_source,catalog,collation_name
    from sys.servers
    ''


select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__script_transfer'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_transfer:

-- ====================================================== sp__script_update_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_update_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140204
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_update_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_update_test') with nowait
        goto skip_sp__script_update_test
        end
    if @ver>140204
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_update_test') with nowait
        goto skip_sp__script_update_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_update_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_update_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_update_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:140204\s.zaglio: little refactor and added not practical test
    v:140203\s.zaglio: added different base code (regular,irregular)
    v:140130\s.zaglio: added tests for not commented and merge update
    v:140116\s.zaglio: added tests for ovr option
    v:131220\s.zaglio: test revision
    v:131217\s.zaglio: another global revision
    v:131216\s.zaglio: remake and relaxed tests
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:''sp__script_update_test @dbg=2''
*/
CREATE proc sp__script_update_test
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @id int,@name sysname,@sql nvarchar(4000),
    @crlf nvarchar(2),@st sysname,
    @msg nvarchar(2000),@log_id int,
    @obj_code nvarchar(max),
    @obj_name sysname,
    @typ char,
    @result sysname

-- =========================================================== initialization ==
select
    @obj_name=''tst_sp__script_update_test_obj'',
    @crlf=crlf
from fn__sym()
-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==

-- =============================================================== #tbls init ==

create table #test(
    id int identity primary key,
    name sysname,
    target_code char(1) check (target_code in (''R'',''I'')),
    sql nvarchar(max),
    opt sysname,
    result char(1),
    status nvarchar(4000) null,
    log_id int null
    )

create table #targets(
    code char,
    sql nvarchar(max)
    )

-- =============================================================== test cases ==

if not object_id(@obj_name) is null exec(''drop proc ''+@obj_name)

-- ##########################
-- ##
-- ## source code
-- ##
-- ########################################################

insert #targets(code,sql)
-- irregular
select ''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131216\s.zaglio: remake and relaxed tests
    v:131216\s.zaglio: not specified hhmm
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
union
-- regular
select ''R'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131216\s.zaglio: not specified hhmm
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''

-- ##########################
-- ##
-- ## upgrade code
-- ##
-- ########################################################

select @opt='''',@obj_code=sql from #targets where code=''I''
-- this first case must be first because the object
-- will be created from case test 2
insert #test(name,opt,result,target_code,sql)
select ''target not exists'',@opt,''N'',''I'',@obj_code
-----------------------------------------------------------
insert #test(name,opt,result,target_code,sql)
select ''target exists and has no comments'',@opt,''U'',''I'',@obj_code
-----------------------------------------------------------
union all select ''daily modification'',@opt,''U'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131216.1000\s.zaglio: remake and relaxed tests with small chg
    v:131216\s.zaglio: not specified hhmm
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''fake daily modification'',@opt,''C'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131216.1000\s.zaglio: changed whole comment
    v:131216\s.zaglio: not specified hhmm
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''older'',@opt,''O'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    todo:calculate new correction dates on last+x
    d:131011\s.zaglio:sp_nothing
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test #1#2#3#4#5,@dbg=2
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''same'',@opt,''S'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131216\s.zaglio: remake and relaxed tests
    v:131216\s.zaglio: not specified hhmm
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''newer for small comment correction'',@opt,''U'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131216\s.zaglio: remake and relaxed tests.
    v:131216\s.zaglio: not specified hhmm.
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''conflict and missin hour'',@opt+''|sd'',''C'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    todo:calculate new correction dates on last+x
    r:131216\s.zaglio: remake and relaxed tests.
    v:131024\s.zaglio: added case with missed hour
    v:131024\s.zaglio: added case 5.
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test #1#2#3#4#5,@dbg=2
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''compile error'',@opt,''E'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131217\s.zaglio: update but with compile error
    v:131216\s.zaglio: remake and relaxed tests
    v:131216\s.zaglio: not specified hhmm
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print hello
''
-----------------------------------------------------------
union all select ''conflict with two missing comment'',@opt,''C'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131217\s.zaglio: update but with compile error
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''older but OVERWRITE'',''ovr|''+@opt,''U'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    todo:calculate new correction dates on last+x
    d:131011\s.zaglio:sp_nothing
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test #1#2#3#4#5,@dbg=2
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''update of merged'',''sd|''+@opt,''U'',''I'',''
/*  leave this
    l:see LICENSE file
    g:utility
    k:upgrade,conflict,author,different,check
    v:131216\s.zaglio: remake and relaxed tests
    v:131216\s.zaglio: not specified hhmm
    v:131216\a.ziglio: merged code
    v:131024\s.zaglio: added case 5
    v:131021\s.zaglio: final release
    r:131018\s.zaglio: added SD option test
    v:131008\s.zaglio: added more case
    r:131007\s.zaglio: test different cases
    t:sp__script_update_test
*/
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------
union all select ''not practical code'',''sd|''+@opt,''E'',''I'',''
create proc ''+@obj_name+''
as
print ''''hello''''
''
-----------------------------------------------------------

-- this is used by #update_log
create table #update_log(
    id int identity,
    dt datetime default(getdate()) not null,
    who sysname default system_user+''@''+isnull(host_name(),''???''),
    obj sysname,
    sts char,    -- Updated,New,Same,Older,Conflict,Error
    msg nvarchar(2000)
    )

-- ===================================================================== body ==

-- drop existing base object of previour bug of test
if not object_id(@obj_name) is null exec(''drop proc ''+@obj_name)

declare cs cursor local for
select
    id,name,sql,opt,result,target_code
from #test
open cs
while 1=1
    begin
    fetch next from cs into @id,@name,@sql,@opt,@result,@typ
    if @@fetch_status!=0 break

    select @obj_code=sql from #targets where code=@typ

    if @dbg>1
        begin
        exec sp__prints ''target code''
        exec sp__printsql @obj_code
        end

    if @dbg>0
        begin
        select @name as [case],@opt as [option],@result as expected
        exec sp__prints ''case %s(%s):%s'',@id,@typ,@name
        end

    -- after test 1, create base the object to modify
    -- case 2 obj without comments
    if @id=2 exec(''create proc ''+@obj_name+'' as print ''''hello'''''')
    if @id>2 exec(@obj_code)

    select @log_id=max(id)+1 from #update_log
    select @log_id=isnull(@log_id,1)

    select @st=cast(@dbg as sysname)
    select @sql=''exec sp__script_update @opt=''''''+@opt+'''''',@dbg=''
               +@st+'',@src=N''''''+replace(@sql,'''''''','''''''''''')+''''''''
    if @dbg>1
        begin
        exec sp__printsql @sql
        exec sp__prints ''result''
        end

    begin try
    exec (@sql)
    select @msg=null
    end try
    begin catch
    select msg=error_message()
    end catch

    -- drop altered object
    exec(''drop proc ''+@obj_name)

    update test set
        log_id=@log_id,
        status=case
               when test.result=log.sts
               then ''ok''+isnull('':''+@msg,'''')
               else ''ko''+isnull('':''+@msg,'''')
               end
    from #test test
    join #update_log log on log.id=@log_id
    where test.id=@id

    end -- cursor cs
close cs
deallocate cs

select @sql=''select ''''#test'''' tbl,log_id,id,name,status from #test''
exec(@sql)
select @sql=''select ''''#update_log'''' tbl,* from #update_log''
exec(@sql)

if exists(select top 1 null from #test where status like ''ko%'')
    raiserror(''test failed'',16,1)

-- ================================================================== dispose ==
dispose:
drop table #test
drop table #update_log

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test different cases for sp__script_update

Parameters
    [param]     [desc]
    @opt        (not used)
    @dbg        passed to sp__script_update

Examples
    sp__script_update_test

-- list of test cases ---
''
exec sp__select_astext ''select id,name from #test order by id''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_update_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_update_test:

-- =============================================== sp__script_utility_setup_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__script_utility_setup_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=140109
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__script_utility_setup_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__script_utility_setup_test') with nowait
        goto skip_sp__script_utility_setup_test
        end
    if @ver>140109
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__script_utility_setup_test') with nowait
        goto skip_sp__script_utility_setup_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__script_utility_setup_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__script_utility_setup_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__script_utility_setup_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:140109\s.zaglio: check
    t:sp__script_utility_setup_test run
*/
CREATE proc sp__script_utility_setup_test
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @run bit

-- =========================================================== initialization ==
select
    @run=charindex(''|run|'',@opt)

-- ======================================================== second params chk ==
if @run=0 goto help


-- =============================================================== #tbls init ==

-- ===================================================================== body ==

-- test if fn__job work in this system
exec @ret=sp__job_test @opt=''setup''


-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test the full functionality of the utility that can fail depending
    from MSSQL version or OS configuration

Parameters
    [param]     [desc]
    @opt        options
                run tun tests
    @dbg        debug level
                1   basic info and do not execute dynamic sql
                2   more details (usually internal tables) and execute dsql
                3   basic info, execute dsql and show remote info

Examples
    sp__script_utility_setup_test run
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__script_utility_setup_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__script_utility_setup_test:

-- =========================================================== sp__select_asform
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__select_asform',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130517
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__select_asform') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__select_asform') with nowait
        goto skip_sp__select_asform
        end
    if @ver>130517
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__select_asform') with nowait
        goto skip_sp__select_asform
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__select_asform') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__select_asform'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__select_asform]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    v:130517\s.zaglio: changed inner #temp tables names; added dot opt
    v:120925\s.zaglio: small bug near @where
    v:120918\s.zaglio: adding option #vars
    v:100919\s.zaglio: adde @p1,... but not well tested
    v:100214\s.zaglio: show 1st record of a table in vertical form
    t:
        select id,name,xtype,crdate
        into #t
        from sysobjects where xtype=''p''
        exec sp__select_asform ''#t'',''name="sp__select_asform"'',@dbg=1
        drop table #t
*/
CREATE proc sp__select_asform
    @tbl sysname=null,
    @where nvarchar(4000)=null,
    @opt sysname=null,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

if @tbl is null goto help

select @opt=dbo.fn__str_quote(@opt,''|''),@ret=0

declare
    @vars bit,@select bit,@sql nvarchar(max),
    @crlf nvarchar(4),@n int,
    @flds nvarchar(4000), @dot bit

select
    @tbl=parsename(@tbl,1),
    @vars=charindex(''|#vars|'',@opt),
    @select=charindex(''|select|'',@opt),
    @dot=charindex(''|dot|'',@opt),
    @where=replace(@where,''"'',''''''''),
    @crlf=crlf,
    @flds=dbo.fn__flds_of(@tbl,'','',null)
from fn__sym()

if not @p1 is null select @tbl=replace(@tbl,''{1}'',convert(sysname,@p1,126)),
                          @where=replace(@where,''{1}'',convert(sysname,@p1,126))
if not @p2 is null select @tbl=replace(@tbl,''{2}'',convert(sysname,@p2,126)),
                          @where=replace(@where,''{2}'',convert(sysname,@p2,126))
if not @p3 is null select @tbl=replace(@tbl,''{3}'',convert(sysname,@p3,126)),
                          @where=replace(@where,''{3}'',convert(sysname,@p3,126))
if not @p4 is null select @tbl=replace(@tbl,''{4}'',convert(sysname,@p4,126)),
                          @where=replace(@where,''{4}'',convert(sysname,@p4,126))

select @where=isnull(''where ''+@where,'''')

create table #sp__select_asform_fld(
    id int identity primary key,
    fld sysname,
    value sql_variant null,
    dsc sysname null
    )

if @vars=1
    insert #sp__select_asform_fld(fld)
    select token as dsc
    from dbo.fn__str_table(@flds,'','')
else
    insert #sp__select_asform_fld(fld,dsc)
    select token,coalesce(com.value,'''') as dsc
    from dbo.fn__str_table(@flds,'','')
    left join (
        select * from dbo.fn__comments(@tbl) where not column_name is null
    ) com on token=com.column_name

-- declare @tbl sysname,@where sysname select @tbl=''sysobjects'',@where=''''

select @sql =isnull(@sql+@crlf,'''')+''when ''''''+fld
            +'''''' then cast(#sp__select_asform_tmp.[''+fld+''] as sql_variant)''
from #sp__select_asform_fld

select @sql =''select top 1 * into #sp__select_asform_tmp from [''+@tbl+''] ''+@crlf
            +@where+@crlf
            +''update #sp__select_asform_fld set value=''+@crlf
            +''case fld''+@crlf
            +@sql+@crlf
            +''end''+@crlf
            +''from #sp__select_asform_tmp''
if @dbg=1 exec sp__printsql @sql
exec(@sql)
if @@error!=0 goto err_cod

if @vars>0
    begin
    insert #vars(id,value)
    select ''%''+f.fld+''%'',f.value
    from #sp__select_asform_fld f left join #vars v on ''%''+f.fld+''%''=v.id
    where v.id is null
    update v set v.value=f.value
    from #sp__select_asform_fld f join #vars v on ''%''+f.fld+''%''=v.id
    end
else
    begin
    if @dot=1
        begin
        select @n=max(len(fld))+2 from #sp__select_asform_fld
        update #sp__select_asform_fld set
            fld=fld+replicate(''.'',@n-len(fld))
        end
    if @select>0
        select id,fld,value,dsc from #sp__select_asform_fld order by id
    else
        exec sp__select_astext
                ''select fld,value,dsc from #sp__select_asform_fld'',
                @opt=@opt
    end

drop table #sp__select_asform_fld
goto ret
-- =================================================================== errors ==
err_cod:    exec @ret=sp__err ''inside code'',@proc goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    print a vertical table with 1st record of @tbl

Parameters
    @tbl    name of table (can be a #temp)
    @where  condition to filter select
    @p1,... replaces {1},{...} into @tbl and @where
    @opt    options
            select  return results as select instead of text
            #vars   out/update table #vars with results
                    as %fld%,value
            h       passed to sp__select_astext
            noh     passed to sp__select_astext
            dot     fill fld column with dots ....
''
select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end -- sp__select_asform'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__select_asform:

-- =========================================================== sp__select_astext
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__select_astext',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130517.1700
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__select_astext') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__select_astext') with nowait
        goto skip_sp__select_astext
        end
    if @ver>130517.1700
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__select_astext') with nowait
        goto skip_sp__select_astext
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__select_astext') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__select_astext'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__select_astext]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    d:130521\s.zaglio:sp__select
    v:130517.1700\s.zaglio: a bug near print into @out and h and noh opts
    v:130424\s.zaglio: again adapted to be multi collate
    v:130422\s.zaglio: removed use of fn__str_print
    v:121025\s.zaglio: better identification of last "from"
    v:121024\s.zaglio: adapted to be multi collate
    v:120830.1518\s.zaglio: a bug near hdr when out ot #tbl or select
    v:120809\s.zaglio: use of fn__sql_normalize
    v:120801\s.zaglio: bug new into ... porder by
    v:120724\s.zaglio: added auto enclose of union into ()
    v:120723.1700\s.zaglio: added footer in html out if @header=2
    v:120723\s.zaglio: chk of unions and a bug generating html file
    v:120720.1531\s.zaglio: converted order by 1,2,... into names
    v:120720.1243\s.zaglio: corrected new and old order by problem
    r:120719\s.zaglio: added html and done test (wrong order)
    r:120718\s.zaglio: a remake for mssql2k5> (20% faster)
    v:120717\s.zaglio: test for -- presence
    v:120307\s.zaglio: better error management
    v:120116\s.zaglio: added #src,#out,p4 options
    v:111130\s.zaglio: when @out=#html or .htm?, now send a real table
    v:110831\s.zaglio: return error if write to file fail
    v:110308\s.zaglio: more help
    v:110219\s.zaglio: added out as select and @sep as varchar32
    v:110112\s.zaglio: added right trim
    v:101201\s.zaglio: added row2,3,4 for very big tables
    v:100911\s.zaglio: added type distinctions between datetime, reals, etc in convert
    v:100718\s.zaglio: added @p1,@p2...
    v:100612\s.zaglio: more debug info
    v:100522\s.zaglio: a bug when @header=0 and added 1 or 2 headers option
    v:100404\s.zaglio: adj. help and added <br>
    v:100328\s.zaglio: removed doubled from run in exec(...)
    v:100228\s.zaglio: added @sep,quoted names and order by
    v:100115\s.zaglio: adjusted header padding and managed 0 rows table
    v:100107\s.zaglio: @out=null -> normal select
    v:100105\s.zaglio: normalized sql
    v:091229\s.zaglio: added help and @header
    v:091227\s.zaglio: show results of tbl/qry as text table with col''autosize
    t:
        exec sp__select_astext ''
            select top 10 id,name [name/test],crdate,cast(0x1221 as image) im
            from sysobjects order by [name/test] desc
            ''
            ,@dbg=1
            ,@opt=''html''
            ,@out=''%temp%\sp__select_astext_test.htm''
        exec master..xp_cmdshell ''type %temp%\sp__select_astext_test.htm''
        exec master..xp_cmdshell ''del %temp%\sp__select_astext_test.htm''
    t:
        create table #src(lno int identity,line nvarchar(4000))
        truncate table #src
        exec sp__select_astext ''
            select name,id [code],xtype,crdate
            from sysobjects
            order by code
            ''
            ,@dbg=2
            ,@out=''#src'',@opt=''html''
        select * from #src
        exec sp__email
            @to=''stefano.zaglio@seltris.it'',
            @body=''#src'',
            @from=''sp__select_astext_test@seltris.it''
        drop table #src
        drop table #t
    t:
        select top 10 * into #t from sysobjects order by crdate desc
        select * from #t
        exec sp__select_astext ''select * from #t''
        exec sp__select_astext ''select * from #t order by id'',@dbg=1
        exec sp__select_astext ''select * from #t order by 2 desc,uid, 1'',@dbg=2
        drop table #t
    t:
        exec sp__select_astext ''
            select name,id [code],xtype,crdate
            from sysobjects
            order by code
            '',@out=''select''
*/
CREATE proc [dbo].[sp__select_astext]
    @what   nvarchar(max)=null,        -- table/view/select
    @out    sysname=null,
    @header tinyint=null,
    @sep    nvarchar(32)=null,
    @p1     sql_variant=null,
    @p2     sql_variant=null,
    @p3     sql_variant=null,
    @p4     sql_variant=null,
    @opt    sysname=null,
    @dbg    int=0
as
begin
set nocount on
declare
    @proc   sysname,
    @ret    int,
    @err    int,
    @id     int,
    @i      int,                        -- index
    @crlf   nvarchar(2),
    @cr     nvarchar(1),
    @lf     nvarchar(1),
    @tab    nvarchar(1),
    @tmp    sysname,                    -- name of ##tmp tables
    @html   bit,                        -- mark out as html
    @csep   nvarchar(255),              -- output column separator
    @chead  nvarchar(255),              -- column head separator
    @cfoot  nvarchar(255),              -- column footer sep.
    @sql    nvarchar(max),              -- dinamyc sql
    @oby    nvarchar(4000),             -- keep orderby clause
    @row    nvarchar(max),              -- final row select format
    @hth    nvarchar(4000),             -- html table header
    @hdr    nvarchar(4000),             -- header tmp string
    @hstyle nvarchar(4000),             -- html table header style
    @q      char(1),                    -- quote char ''
    @dq     char(2),                    -- double quote char ''''
    @declare_end bit

declare @src table(lno int identity primary key,line nvarchar(4000))

select
    @proc=object_name(@@procid),@crlf=crlf,@ret=0,
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|''),
    @cr=cr,@lf=lf,@tab=tab,@crlf=crlf
from dbo.fn__sym()

if @what is null goto help

if charindex(''--'',@what)>0 goto err_lce

-- normalize sql
select @what=dbo.fn__sql_normalize(@what,''sel'')

-- replace parameters
if right(@what,1)=''='' select @what=@what+convert(sysname,@p1,126),@p1=null
if not @p1 is null select @what=replace(@what,''{1}'',convert(sysname,@p1,126))
if not @p2 is null select @what=replace(@what,''{2}'',convert(sysname,@p2,126))
if not @p3 is null select @what=replace(@what,''{3}'',convert(sysname,@p3,126))
if not @p4 is null select @what=replace(@what,''{4}'',convert(sysname,@p4,126))
-- 120718\s.zaglio: aligned to sp__usage
if not @p1 is null select @what=replace(@what,''%p1%'',convert(sysname,@p1,126))
if not @p2 is null select @what=replace(@what,''%p2%'',convert(sysname,@p2,126))
if not @p3 is null select @what=replace(@what,''%p3%'',convert(sysname,@p3,126))
if not @p4 is null select @what=replace(@what,''%p4%'',convert(sysname,@p4,126))

if @dbg>0 exec sp__printsql @what

create table #html (lno int identity,line nvarchar(4000))   -- for html output
create table #cols (
    col sysname,                                            -- name
    typ sysname,                                            -- type
    pos smallint identity primary key,                      -- colord
    spos as cast(pos as varchar(5)),                        -- pos as string
    width smallint,                                         -- max col width
    swidth as cast(width as varchar(5)),                    -- width as string
    cst nvarchar(512),                                      -- cast code
    csep bit                                                -- if column separator
    )

-- init constants/vars
select
    @q     ='''''''',
    @dq    ='''''''''''',
    @html  =case
            when charindex(''|html|'',@opt)>0 then 1
            when right(@out,4)=''.htm'' then 1
            when right(@out,5)=''.html'' then 1
            else 0
            end,
    @header=isnull(@header,
            case
            when charindex(''|h|'',@opt)>0 then 1
            when charindex(''|noh|'',@opt)>0 then 0
            else 2
            end),
    @csep  =case @html
            when 1 then ''</td><td>''
            else isnull(replace(@sep,@q,@dq),'' '')
            end,
    @chead =case @html when 1 then ''<tr><td>'' else '''' end,
    @cfoot =case @html when 1 then ''</td></tr>'' else '''' end,
    @hth   =''<table width="100%" ''+
            ''border="1" width="100%" cellspacing="0" cellpadding="2" ''+
            ''style="border-collapse: collapse;''+
            ''font-size:10pt;font-family:arial''+
            ''">'',
    @hstyle=''background-color:darkgray;color:white;text-align:center;''+
            ''font-size:120%;font-size:120%;font-weight:bold;font-style:italic''

-- hive off sql pieces of code
select @i = charindex('' order by '',@what)
if @i>0
    select @oby=substring(@what,@i+10,4000),@what=left(@what,@i)
else
    select @oby=''''

select @i = charindex('' union '',@what)
if @i>0
    begin
    if charindex(''('',@what)>@i
    or charindex('')'',@what,@i)=0
        select @what=''select * from (''+@what+'') a''
    end

select @i = dbo.fn__charindex('' from '',@what,-1) -- @i is used below near INTO
if @i=0 goto err_from

if charindex(''|#src|'',@opt)>0 select @out=''#src''
if charindex(''|#out|'',@opt)>0 select @out=''#out''
if charindex(''|p4|'',  @opt)>0 select @out=convert(nvarchar(512),@p4)

-- create tmp table 1
select
    @tmp=''##tmp''+replace(convert(sysname,newid()),''-'',''''),
    @sql=left(@what,@i)+'' into ''+@tmp
        +substring(@what,@i,len(@what))
        +case @oby when '''' then '''' else '' order by ''+@oby end
exec(@sql)
select @err=@@error,@id=@@identity

if @err!=0
    begin
    exec sp__printf @sql
    goto err_sql
    end
if @dbg>0 exec sp__printsql @sql

-- get flds info
insert #cols(col,typ,csep,width)
select c.name,t.name,1,len(c.name)
from tempdb..syscolumns c
join tempdb..systypes t
on c.xusertype=t.xusertype
where c.id=object_id(''tempdb..''+@tmp)
and t.name!=''image''
update #cols set csep=0 where pos=@@identity

-- due the final select a+b+c.. the order by n fail because n can be only 1
-- we split order by fields and replace it with names
if @oby!=''''
    begin
    declare @obyf table(
        pos int,
        fld nvarchar(4000),
        nfld nvarchar(4000),
        ord sysname,
        sep char(1)
        )
    insert @obyf(
        pos,
        fld,
        ord,
        sep
        )
    select
        pos,
        dbo.fn__str_at(ltrim(rtrim(token)),'' '',1),
        isnull(dbo.fn__str_at(ltrim(rtrim(token)),'' '',2),''''),
        '',''
    from dbo.fn__str_table(@oby,'','')
    order by pos
    update @obyf set sep='''' where pos=(select max(pos) from @obyf)

    if @dbg>0 exec sp__printf ''@oby:%s'',@oby

    update tbl set
        nfld=(select a.col
              from #cols a
              where a.spos=tbl.fld collate database_default)
    from @obyf tbl
    where isnumeric(tbl.fld)=1

    -- recompound clause
    select @oby=''''
    select @oby=@oby+isnull(nfld,fld)+'' ''+ord+sep from @obyf order by pos
    select @oby='' order by ''+@oby

    if @dbg>1 select ''@obyf'' tbl,* from @obyf
    if @dbg>0 exec sp__printf ''@oby:%s'',@oby

    end -- order by

-- set casting (note: can be optimized more)
update #cols set cst=
    ''isnull(''+case
    when typ in (''text'',''ntext'')
    then ''substring(''+quotename(col)+'',1,4000)''
    when typ like ''%char''
    then quotename(col)
    when typ like ''%date%'' or typ like ''%time%''
    then ''convert(nvarchar(4000),''+quotename(col)+'',126)''
    else ''cast(''+quotename(col)+'' as nvarchar(4000))''
    end+'','''''''')''

-- calculate max width of each column
select @sql=null
select @sql=isnull(@sql+'','',''declare '')+''@c''+spos+'' smallint''
from #cols
select @sql=@sql++@crlf+''select ''
select @sql=@sql+'' @c''+spos+''=max(len(''+cst+''))''
                +case csep when 1 then '','' else '''' end
from #cols
select @sql=@sql+''from ''+@tmp+@crlf
select @sql=@sql+''update #cols set width=''
                +''case ''+
                +''when @c''+spos+''>width then @c''+spos+'' ''
                +''else width end ''    -- if width of header is greater
                +''where pos=''+spos+@crlf
from #cols

if @dbg>0 exec sp__printsql @sql
exec(@sql)
select @err=@@error

if @err!=0
    begin
    exec sp__printf @sql
    goto err_sql
    end


if @dbg>1 select ''#col'' tbl,* from #cols

-- prepare header table for txt out
if @html=0
    begin
    select @hdr=''''
    select @hdr=@hdr+left(col+replicate('' '',width),width)
               +case csep when 1 then @csep else '''' end
    from #cols
    order by pos
    select @hdr=rtrim(@hdr)             -- optimization
    end -- txt headers

-- =================================================================== output ==

-- compound final select row
select @row=''''
if @html=1
    select  @row = @row
            +cst
            +case csep when 1 then ''+''''''+@csep+''''''+'' else '''' end
    from #cols
    order by pos
else
    select  @row = @row
            +case swidth
             when ''0'' then ''''
             else ''cast(''+cst+'' as nchar(''+swidth+''))''
             end
            +case csep when 1 then ''+''''''+@csep+''''''+'' else '''' end
    from #cols
    order by pos

-- add row header and footer
select @row=@q+@chead+''''''+''+@row+''+''''''+@cfoot+@q

if @dbg>0 exec sp__printsql @row

-- output middle table if dbg
if @dbg>1 exec(''select ''+@row+'' from ''+@tmp+@oby)

if @dbg>0 exec sp__printf ''-- print''

if @html=0
    begin
    if @out is null
        begin
        if @header in (1,2) print @hdr
        select @sql=''
        declare @line nvarchar(4000)
        declare c cursor local for
        select rtrim(''+@row+'') collate database_default as line
        from ''+@tmp+@oby+''
        open c
        while (1=1)
            begin
            fetch next from c into @line
            if @@fetch_status!=0 break
            -- print dbo.fn__str_print(@line)
            print @line
            end
        close c
        deallocate c
        ''
        if @dbg>0 exec sp__printf @sql
        exec(@sql)
        select @err=@@error
        if @err!=0
            begin
            exec sp__printf @sql
            goto err_sql
            end

        if @header=2 print @hdr
        end -- print
    else
        begin
        if @out=''select''
            begin
            if @header in (1,2) insert @src select @hdr
            insert @src exec(''select rtrim(''+@row+'') line from ''+@tmp+@oby)
            if @header in (1,2) insert @src select @hdr
            select line from @src order by lno
            end
        else -- insert to table
            begin
            select @sql=''''
            if @header in (1,2)
                select @sql=''insert into ''+@out+''(line) ''
                           +''values(''''''+replace(@hdr,'''''''','''''''''''')+'''''')''
                           +@crlf
            select @sql = @sql
                        + '';insert into ''+@out+''(line) ''
                        + ''select rtrim(''+@row+'') as line ''
                        + ''from ''+@tmp+@oby
            if @header in (1,2)
                select @sql=@crlf+@sql+
                           +'';insert into ''+@out+''(line) ''+
                           +''values(''''''+replace(@hdr,'''''''','''''''''''')+'''''')''
            exec(@sql)
            select @err=@@error

            if @err!=0
                begin
                exec sp__printsql @sql
                goto err_sql
                end

            end
        end -- @out
    end -- no html
else
    begin  -- is html
    insert #html select @hth

    -- insert header
    select @hdr=''''
    select @hdr=@hdr+''<th>''+col+''</th>'' from #cols order by pos
    select @hdr=''<thead style="''+@hstyle+''"><tr>''+@hdr+''</tr></thead>''
    insert #html select @hdr

    select @sql =''insert into #html(line) ''
                +''select ''+@row+'' as line ''
                +''from ''+@tmp+@oby
    exec(@sql)
    select @err=@@error,@id=@@identity
    if @header=2
        update #html set line=replace(line,''<tr>'',''<tr style="''+@hstyle+''">'')
        where lno=@id

    if @err!=0
        begin
        exec sp__printf @sql
        goto err_sql
        end

    insert #html select ''</table>''
    if @out is null
        begin
        if @dbg>0 exec sp__printf ''-- out to:text as html''
        exec sp__print_table ''#html''
        end
    else
        begin
        if @out=''select''
            begin
            if @dbg>0 exec sp__printf ''-- out to:select as html''
            exec(''select line from #html order by lno'')
            end
        else
            begin
            if left(@out,1)=''#''
                begin
                if @dbg>0 exec sp__printf ''-- out to:#table as html''
                select @sql =''insert into ''+@out+''(line) ''
                            +''select line ''
                            +''from #html ''
                            +''order by lno''
                exec(@sql)
                select @err=@@error

                if @err!=0
                    begin
                    exec sp__printf @sql
                    goto err_sql
                    end

                end
            else
                begin
                if @dbg>0 exec sp__printf ''-- out to:file as html''
                exec @ret=sp__file_write @out,@table=''#html''
                -- the below add chars at top of text that are not
                -- correctly readed
                -- exec @ret=sp__file_write_stream @out,@opt=''html''
                end
            end
        end
    end -- out

if @err!=0 goto err

dispose:
drop table #html
exec(''drop table ''+@tmp)

goto ret

-- =================================================================== errors ==
err:        goto ret
err_len:    exec @ret=sp__err ''null len'',@proc goto ret
err_ofm:    exec @ret=sp__err ''out of memory string'',@proc goto ret
err_lce:    exec @ret=sp__err ''line comment -- illegal'',@proc goto ret
err_from:   exec @ret=sp__err ''cluase FROM absent'',@proc goto ret
err_sql:    exec @ret=sp__err ''bad sql code'',@proc goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''

Parameters:
    @what   can be a table or a query (use top clause)
    @out    can be a #table or .htm/.html file
            if "select", return as select
    @p1...  replace "%p1%","%p2%",... in @what befor execute
            (retro-compatible with {1},{2}...)
    @header default is 2 and
            * when txt out:
            0 do not print
            1 print header as name of columns
            2 print footer as name of columns
            * when htm out:
            0 do not add headers
            1 add highlighted header as name of columns
            2 highlight last row
    @opt    options
            html    return a <table>...</table> structure
                    * can be combined with @out
                    * implicit if @out end with .htm...
            #src    set out to this table
            #out    set out to this table
            p4      (experimental)set @out to @p4
            h       show only top header
            noh     do not show any header
    @dbg    1 print inside code
            2 show mid table content

Notes
    * order by is applied on result query so in:
        select fld1 as [a] from tbl order by fld1
      must become:
        select fld1 as myname from tbl order by myname
    * FROM clause must be present
''
select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end -- sp__select_astext'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__select_astext:

-- =============================================================== sp__server_ip
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__server_ip',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131020
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__server_ip') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__server_ip') with nowait
        goto skip_sp__server_ip
        end
    if @ver>131020
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__server_ip') with nowait
        goto skip_sp__server_ip
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__server_ip') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__server_ip'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__server_ip]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:131020\s.zaglio: get server ip
    t:
        create proc tst_server_ip
        as declare @ip sysname exec sp__server_ip @ip out print ''-''+@ip
        go
        exec tst_server_ip
        drop proc tst_server_ip
*/
CREATE proc sp__server_ip(@ip sysname=null out)
as
begin
declare @cmd sysname
declare @out table(line sysname null)
select @cmd=''@for /f "tokens=5 delims= " %d ''
           +''in (''''ping %srv% -4 -n 1 ^| find /i "ping %srv%"'''') ''
           +''do @echo %d''
select @cmd=replace(@cmd,''%srv%'',cast(serverproperty(''machinename'') as sysname))
insert @out exec xp_cmdshell @cmd
select top 1 @ip=substring(line,2,len(line)-2) from @out
if @@nestlevel=1 and dbo.fn__isconsole()=1 print @ip
end -- sp__server_ip'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__server_ip:

-- ================================================================ sp__snapshot
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__snapshot',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=111116
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__snapshot') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__snapshot') with nowait
        goto skip_sp__snapshot
        end
    if @ver>111116
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__snapshot') with nowait
        goto skip_sp__snapshot
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__snapshot') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__snapshot'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__snapshot]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:111116\s.zaglio:removed use of fn__format
    v:111115\s.zaglio:create quickly a snapshot of a db
    t:sp__snapshot #,@dbg=1
*/
CREATE proc sp__snapshot
    @db sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==
if @opt is null or @db is null goto help
if @db!=''#''
and not exists(select null from master..sysdatabases where [name]=@db)
    goto err_ndb
if @db=''#'' select @db=db_name()

-- ============================================================== declaration ==
declare
    @sql nvarchar(max),
    @ndb sysname,@dt nvarchar(32),
    @psep nvarchar(4),@d datetime
create table #src(lno int identity, line nvarchar(4000))
create table #files(
    fileid int,
    [name] sysname,
    [path] nvarchar(512),
    [file] sysname,
    [ext] sysname
    )
-- =========================================================== initialization ==
select
    @psep=psep,
    @d=getdate(),
    @dt=convert(nvarchar(32),@d,8),
    @dt=convert(nvarchar(32),@d,12)+''_''+substring(@dt,1,2)+substring(@dt,4,2),
    @ndb=quotename(@db+''_''+@dt),
    @db=quotename(@db)
-- select *
from fn__sym()
-- ======================================================== second params chk ==
-- ===================================================================== body ==

-- collect files of db
select @sql=''
insert #files(fileid,[name],[path],[file],ext)
select
    fileid,
    [name],
    substring([filename],1,(len([filename])-charindex(psep,reverse([filename])))+1) as [path],
    substring([filename],(len([filename])-charindex(psep,reverse([filename])))+2,128) as [file],
    substring([filename],(len([filename])-charindex(''''.'''',reverse([filename])))+2,128) as [ext]
from fn__sym(),''+@db+''..sysfiles sf
where status & 0x40 = 0;
update #files set [file]=substring([file],1,len([file])-len([ext])-1)
''
exec (@sql)
if @@error!=0 exec sp__printsql @sql

if not exists(select top 1 null from #files) goto err_nof

insert #src(line) select ''create database ''+@ndb+'' on (''
insert #src(line)
select ''    name = ''+[name]+'', filename =''+crlf+
       ''    ''''''+[path]+[file]+''_''+@dt+''.ss'''' ''+crlf
from fn__sym(),#files
insert #src(line) select '') as snapshot of ''+@db

if @dbg>0
    exec sp__print_table ''#src''
else
    exec @ret=sp__script_compile
if @@error=0 and @ret=0 exec sp__printf ''-- drop database %s'',@ndb

drop table #src
drop table #files

goto ret

-- =================================================================== errors ==
err_ndb: exec @ret=sp__err ''database "%s" not found'',@proc,@p1=@db goto ret
err_nof: exec @ret=sp__err ''database "%s" with no files?'',@proc,@p1=@db goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    create quickly a snapshot of a db into the same data directory
    with same name and extension YYMMDD_HHMMSS

Parameters
    @db     name of database (# for current)
    @dbg    debug options
            1 print sql instead of execute

Examples
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__snapshot'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__snapshot:

-- ================================================================ sp__str_join
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__str_join',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=081114
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__str_join') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__str_join') with nowait
        goto skip_sp__str_join
        end
    if @ver>081114
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__str_join') with nowait
        goto skip_sp__str_join
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__str_join') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__str_join'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__str_join]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:081114\S.Zaglio: join strings with a @separator. PArameters must not null
    t:
        begin
        declare @s nvarchar(4000)
        exec sp__str_join @s out,''|'',''1'',''a'',''2'',''b'',@test=1
        exec sp__str_join @s out,''|'',''c'',''1'',''2'',''b'',@test=1 -- test auto resets
        exec sp__str_join @s out,''|'',@s,''d'',''3'',''4'',''e'',@test=1 -- test keep
        end
*/
CREATE  proc sp__str_join
    @sentence nvarchar(4000) out,
    @sep nvarchar(32),
    @v1 sql_variant,
    @v2 sql_Variant=null,
    @v3 sql_Variant=null,
    @v4 sql_Variant=null,
    @v5 sql_Variant=null,
    @v6 sql_Variant=null,
    @v7 sql_Variant=null,
    @v8 sql_Variant=null,
    @v9 sql_Variant=null,
    @v10 sql_Variant=null,
    @v11 sql_Variant=null,
    @v12 sql_Variant=null,
    @v13 sql_Variant=null,
    @v14 sql_Variant=null,
    @v15 sql_Variant=null,
    @v16 sql_Variant=null,
    @test bit=0
as
begin
declare @n int, @i int
declare @v sql_variant
set @i=1 set @n=16
while (@i<=@n) begin
    if @i=1 set @v=@v1
    if @i=2 set @v=@v2
    if @i=3 set @v=@v3
    if @i=4 set @v=@v4
    if @i=5 set @v=@v5
    if @i=6 set @v=@v6
    if @i=7 set @v=@v7
    if @i=8 set @v=@v8
    if @i=9 set @v=@v9
    if @i=10 set @v=@v10
    if @i=11 set @v=@v11
    if @i=12 set @v=@v12
    if @i=13 set @v=@v13
    if @i=14 set @v=@v14
    if @i=15 set @v=@v15
    if @i=16 set @v=@v16
    if @v is null break
    if @i=1 set @sentence=''''
    if @sentence<>'''' set @sentence=@sentence+@sep
    set @sentence=@sentence+convert(nvarchar(4000),@v)
    set @i=@i+1
end -- while
if @test=1 print @sentence
end -- fn__str_replace'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__str_join:

-- =============================================================== sp__str_print
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__str_print',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100404
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__str_print') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__str_print') with nowait
        goto skip_sp__str_print
        end
    if @ver>100404
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__str_print') with nowait
        goto skip_sp__str_print
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__str_print') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__str_print'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__str_print]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100404\s.zaglio: print tokens tabbed
    t:sp__str_print ''one,two,longname,first_name,last_name,this is a sentence,test 1234''
                    ,'','',@indent=''    ''
                    ,@out=''#src''
*/
create proc sp__str_print
    @objs nvarchar(4000),@sep nvarchar(32)=''|'',
    @indent sysname='''',
    @out sysname=null,
    @len int=80
as
begin
set nocount on
declare @n int,@ll int,@txt nvarchar(4000),@k int,@crlf nvarchar(2)

select @crlf=crlf from dbo.fn__sym()

declare @tkns table(pos int,token sysname,sep nvarchar(32))
insert @tkns select pos,token,@sep from dbo.fn__str_table(@objs,@sep)

select @len=@len-len(@indent)
select @n=max(len(token))+1,@k=max(pos) from @tkns
select @txt=null,@ll=((@len/@n*@n)+2)

update @tkns set sep='''' where pos=@k

select @txt =coalesce(@txt,@indent)+left(token+sep+replicate('' '',@n),@n)
            +case when pos%(@len/@n)=0 then @crlf+@indent else ''  '' end
from @tkns
order by pos

if @out is null
    print @txt
else
    begin
    select @txt=''insert ''+@out+'' select ''''''+replace(@txt,'''''''','''''''''''')+''''''''
    exec(@txt)
    end

end -- sp__str_print'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__str_print:

-- ============================================================= sp__str_replace
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__str_replace',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120416
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__str_replace') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__str_replace') with nowait
        goto skip_sp__str_replace
        end
    if @ver>120416
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__str_replace') with nowait
        goto skip_sp__str_replace
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__str_replace') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__str_replace'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__str_replace]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120416\s.zaglio: @sentence to nvarchar(max)
    v:110823\s.zaglio: better help
    v:110512\s.zaglio: a bug near @tbl
    v:110415\s.zaglio: a bug on inline multiple tags
    v:110316\s.zaglio: a remake
    v:091029\s.zaglio: management of error within len(token)>16 now 32
    v:091025\s.zaglio: a little remake using @tables; @tbl is changed to bit and used fixed name #vars
    v:091022\s.zaglio: expanded limits of 16 tockens if @tbl not null
    v:091016.2000\s.zaglio: added help
    v:090918\s.zaglio: restyle
    v:090610\S.Zaglio: added @sep
    v:090123\S.Zaglio: added @null parameters for null constant for null values
    v:090121\S.Zaglio: added imput data from table (##)
    v:090113\S.Zaglio: added max tokens check
    v:081219\S.Zaglio: added @inject for special sql replaces
    v:081214\S.Zaglio: changed again convert to 126 because damn MSSQL
    v:081212\S.Zaglio: changed convert date from 126 to 120 because MSSQL QA 2k-2k8 don''t accept T
    v:081130\S.Zaglio: added auto convertion of datetime values to iso8601
    v:081016\S.Zaglio: removed @from and added check for multi or single tokens
    v:081007\S.Zaglio: added @from as costant source for  @sentence
    v:080909\S.Zaglio: if @tokens is null become eq to @sentence and work like a fn__str_join
    v:080815\S.Zaglio: added spaces trim on tokens to allow well format coding and @v9-@v16
    v:080807\S.Zaglio: added @test,@v5-7 and correcteed bug in convertion
    v:080729\S.Zaglio: multiple replace
*/
CREATE proc [dbo].[sp__str_replace]
    @sentence nvarchar(max)=null out,
    @tokens nvarchar(4000)=null,
    @v1 sql_variant=null,
    @v2 sql_variant=null,
    @v3 sql_variant=null,
    @v4 sql_variant=null,
    @v5 sql_variant=null,
    @v6 sql_variant=null,
    @v7 sql_variant=null,
    @v8 sql_variant=null,
    @v9 sql_variant=null,
    @v10 sql_variant=null,
    @v11 sql_variant=null,
    @v12 sql_variant=null,
    @v13 sql_variant=null,
    @v14 sql_variant=null,
    @v15 sql_variant=null,
    @v16 sql_variant=null,
    @test bit=null,
    @dstyle tinyint=null,
    @inject bit=null,
    @tbl bit=null,
    @null sysname=null,
    @sep sysname=null,
    @dbg bit=null
as
begin
set nocount on

declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

if @sentence is null goto help

select
    @test   =isnull(@test,0),
    @dstyle =isnull(@dstyle,126),
    @inject =isnull(@inject,0),
    @tbl    =isnull(@tbl,0),
    @null   =isnull(@null,''''),
    @sep    =isnull(@sep,''|''),
    @dbg    =isnull(@dbg,0)

declare
    @n int,@i int, @v sql_variant,@tl smallint, -- max token name length
    @vv nvarchar(4000),@t sysname, @single bit,
    @token nvarchar(4000)

-- a id bigger than 16 cause mssql warning about rec size>8096 byes
declare @tkns table (
    id int identity(1,1) primary key,
    tkn nvarchar(4000),
    val sql_variant null
    )

select @tl=32

if @tokens=''#vars''
    insert @tkns(tkn,val)
    select id,[value] from #vars

if @tbl=1
    insert @tkns(tkn,val)
    select id,[value] from #vars v
    -- 110512\s.zaglio
    -- join dbo.fn__str_table(@tokens,@sep) t
    -- on t.token=v.id

if @tokens!=''#vars'' and @tbl!=1
    begin
    insert @tkns(tkn)
    select left(token,@tl)
    from dbo.fn__str_table(@tokens,@sep) t
    if exists(select null from @tkns where len(tkn)>@tl) goto err_tln
    update @tkns set val=@v1 where id=1
    update @tkns set val=@v2 where id=2
    update @tkns set val=@v3 where id=3
    update @tkns set val=@v4 where id=4
    update @tkns set val=@v5 where id=5
    update @tkns set val=@v6 where id=6
    update @tkns set val=@v7 where id=7
    update @tkns set val=@v8 where id=8
    update @tkns set val=@v9 where id=9
    update @tkns set val=@v10 where id=10
    update @tkns set val=@v11 where id=11
    update @tkns set val=@v12 where id=12
    update @tkns set val=@v13 where id=13
    update @tkns set val=@v14 where id=14
    update @tkns set val=@v15 where id=15
    update @tkns set val=@v16 where id=16
    end -- if @tokens!=''#vars'' and @tbl!=1

-- convertion to string
update @tkns set
    val=case
        when SQL_VARIANT_PROPERTY(val,''BaseType'')=''datetime''
        then convert(nvarchar(48),val,@dstyle)
        else convert(nvarchar(4000),coalesce(val,@null))
        end


if @dbg=1 select * from @tkns

if @sentence!=''#src''
    update @tkns set
        @sentence=replace(@sentence,tkn,convert(nvarchar(4000),val))
    from @tkns
else
    begin
    declare cs cursor local forward_only for
        select lno,line
        from #src
    open cs
    while 1=1
        begin
        fetch next from cs into @i,@vv
        if @@fetch_status!=0 break
        select
            @vv=replace(@vv,tkn,convert(nvarchar(4000),val))
        from @tkns t
        where charindex(t.tkn,@vv)>0
        update #src set line=@vv where lno=@i
        end -- while of cursor
    close cs
    deallocate cs
    end -- #src replace

if @test=1
    begin
    if @sentence!=''#src''
        print @sentence
    else
        exec sp__print_table ''#src''
    end
goto ret
-- =================================================================== errors ==
err_tln:    exec sp__err ''token name greater than %s'',@proc,@p1=@tl
            goto ret
-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    replace pieces of sentence

Parameters
    @sentence   the source to replace (or #src)
    #src        alternative source (lno int identity,line nvarchar(4000))
    @tokens     the pieces to replace separated by @sep (or #vars)
    @v1..@v16   the values relative to tokens
    #vars       alternative tokens (id nvarchar(16),value sql_variant)
    @test       print the result
    @dstyle     date style (default 126)
    @inject     inject the values into ''''''''
    @tbl        if 1, uses content of #vars instead of @v1..v16
    @null       replace nulls values with this
    @sep        by default "|", is the separator of tokens
    @dbg        debug mode; print some info for developer

Examples:

    declare @r real,@d datetime
    select @r=10.12345678,@d=getdate()
    exec sp__str_replace    ''''%s;r=%r;d=%d'''',
                            ''''%r|%d|%s'''',
                            @r,@d,''''replaced''''
                            ,@test=1

    exec sp__str_replace    ''''this %is% the %n%st %tst%'''',
                            ''''%is%|%n%|%tst%'''',
                            ''''is'''',1  ,''''test''''
                            ,@test=1

    table mode 1:
        create table #vars (id nvarchar(16),value sql_variant)
        declare @st sysname
        insert #vars values(''''%a%'''',''''STEFANO'''')
        insert #vars values(''''%d%'''',getdate())
        set @st=''''my name is %a% and now is %d% local time''''
        exec sp__str_replace @st out,''''%a%|%d%'''',@tbl=1,@test=1
        drop table #vars

    table mode 2:
        create table #src  (lno int identity,line nvarchar(4000))
        create table #vars (id nvarchar(16),value sql_variant)

        insert #src(line) select ''''this %is% test''''
        insert #src(line) select ''''of the %dt%''''
        insert #vars values(''''%is%'''',''''is'''')
        insert #vars values(''''%dt%'''',getdate())
        exec sp__str_replace ''''#src'''',''''#vars'''',@test=1,@dbg=1
        drop table #vars
        drop table #src

''
select @ret=-1

ret:
return @ret
end -- sp__str_replace'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__str_replace:

-- ================================================================ sp__str_swap
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__str_swap',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=081212
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__str_swap') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__str_swap') with nowait
        goto skip_sp__str_swap
        end
    if @ver>081212
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__str_swap') with nowait
        goto skip_sp__str_swap
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__str_swap') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__str_swap'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__str_swap]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:081212\S.Zaglio: swap two varaibles
    t:begin declare @a sysname,@b sysname set @a=''a'' set @b=''b'' exec sp__str_swap @a out,@b out print @a print @b end
*/
CREATE  proc sp__str_swap @a nvarchar(4000) out,@b nvarchar(4000) out
as
begin
declare @t nvarchar(4000)
set @t=@a set @a=@b set @b=@t
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__str_swap:

-- =============================================================== sp__str_table
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__str_table',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100508
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__str_table') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__str_table') with nowait
        goto skip_sp__str_table
        end
    if @ver>100508
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__str_table') with nowait
        goto skip_sp__str_table
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__str_table') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__str_table'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__str_table]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100508\s.zaglio: adde hwhere parameters with @p1,@p2,...
    v:100113\s.zaglio: added test comment
    v:091027\s.zaglio: name that start with #,@ are not closed into % and managed (n)text flds
    v:091018\s.zaglio: verticalize a row of a table into a #vars for str_replace
    t:
        create table #vars (id nvarchar(16),value sql_variant)
        create table #local(id int,name sysname)
        insert #local select 1,''one''
        insert #local select 2,''two''
        exec sp__str_table ''#vars'',''#local'',''id=%d'',@p1=2-- ,@dbg=1
        declare @sql sysname select @sql=''id:%id%; name:%name%''
        exec sp__str_replace @sql out,@tbl=1
        print @sql
        drop table #local drop table #vars
*/
CREATE proc [dbo].[sp__str_table]
    @vtbl sysname=null,
    @htbl sysname=null,
    @hwhere sysname=null,
    @excludes sysname=null,
    @p1 sql_variant=null,
    @p2 sql_variant=null,
    @p3 sql_variant=null,
    @p4 sql_variant=null,
    @dbg bit=0
as
begin
set nocount on
declare
    @flds nvarchar(4000),@types nvarchar(4000),
    @declares nvarchar(4000),@select nvarchar(4000),
    @inserts nvarchar(4000),@crlf nchar(2),@sql nvarchar(4000),
    @i int,@n int,@fld sysname,@type sysname,@sfld sysname

if @vtbl is null or @htbl is null goto help

select
    @crlf=char(13)+char(10),
    @flds =dbo.fn__flds_of(@htbl,''|'',null),
    @types=dbo.fn__flds_type_of(@htbl,''|'',null),
    @select='''',@declares='''',@inserts='''',
    @i=1,@n=dbo.fn__str_count(@flds,''|'')

while (@i<=@n)
    begin
    select @fld =dbo.fn__str_at(@flds ,''|'',@i)
    select @sfld=quotename(@fld)
    select @type=dbo.fn__str_at(@types,''|'',@i)
    if @type in (''text'',''ntext'') select @sfld=''convert(nvarchar(4000),''+@sfld+'') ''
    if @type in (''text'',''ntext'') select @type=''nvarchar(4000)''
    if dbo.fn__at(@fld,@excludes,''|'')=0
        begin
        select @declares=@declares+''@''+@fld+'' ''+@type+case when @i<@n then '','' else '''' end+@crlf
        select @select=@select+''@''+@fld+''=''+@sfld+case when @i<@n then '','' else '''' end
        select @inserts=@inserts+''insert ''+@vtbl+'' select ''''''
            +case when left(@fld,1) in (''#'',''@'') then @fld else ''%''+@fld+''%'' end
            +'''''',@''+@fld+@crlf
        end
    select @i=@i+1
    end

select @sql=''truncate table ''+@vtbl+@crlf
           +''declare''+@crlf+@declares
           +''select top 1 ''+@select+'' from ''+@htbl+@crlf
           +coalesce(''where ''
            +dbo.fn__printf(@hwhere,@p1,@p2,@p3,@p4,null,null,null,null,null,null)
            ,'''')
           +@crlf
           +@inserts
if @dbg=1 exec sp__printf @sql
exec(@sql)
goto ret
help:
select @sql=''Verticalize a row of a table into a table to pass to sp__str_replace
Sample:
        create table #vars(id nvarchar(16), value sql_variant)
        create table #objs(id int,name sysname,age int,b bit, e nvarchar(10), t ntext)
        insert #objs select 1,''''first'''',10,1,''''v10'''',''''text1''''
        insert #objs select 2,''''second'''',20,2,''''v20'''',''''text2''''
        exec sp__str_table ''''#vars'''',''''#objs'''',''''id=%d'''',@p1=1
        select * from #vars
        drop table #vars
        drop table #objs''
exec sp__usage ''sp__str_table'',@sql
ret:
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__str_table:

-- ========================================================== sp__str_table_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__str_table_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130925
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__str_table_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__str_table_test') with nowait
        goto skip_sp__str_table_test
        end
    if @ver>130925
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__str_table_test') with nowait
        goto skip_sp__str_table_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__str_table_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__str_table_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__str_table_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:130925\s.zaglio:test for fn__str_table
*/
CREATE proc sp__str_table_test
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare @test table(tst nvarchar(max),pos int,token nvarchar(4000))
insert @test select ''a|b|cc'',0,''|''  -- main test
insert @test select ''a|b|cc'',1,''a''  -- result
insert @test select ''a|b|cc'',2,''b''  -- result
insert @test select ''a|b|cc'',3,''cc'' -- result
insert @test select ''a\n\nc'',0,''\n'' -- main test
insert @test select ''a\n\nc'',1,''a''
insert @test select ''a\n\nc'',2,''''
insert @test select ''a\n\nc'',3,''c''
insert @test select ''a b c  d'',0,'''' -- main test
insert @test select ''a b c  d'',1,''a''
insert @test select ''a b c  d'',2,''b''
insert @test select ''a b c  d'',3,''c''
insert @test select ''a b c  d'',4,''''
insert @test select ''a b c  d'',5,''d''
-- =========================================================== initialization ==
-- ======================================================== second params chk ==
-- =============================================================== #tbls init ==
-- ===================================================================== body ==
select
    case when tst.token=t.token then ''ok'' else ''ko'' end sts,
    tst.tst,tst.pos,tst.token tst_token,t.token fn_result
into #t
from @test tst
full outer join (
    select t.tst,f.pos,f.token
    from @test t
    cross apply fn__str_table(tst,token) f
    where t.pos=0
    ) t
on tst.tst=t.tst and tst.pos=t.pos
where tst.pos!=0

exec sp__select_astext ''select * from #t order by 1,2,3''

exec sp__printf ''''
exec sp__prints ''8<''
exec sp__printf ''''

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.


-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test for fn__str_table

Parameters
    [param]     [desc]
    @opt        (not used)
    @dbg        (not used)

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__str_table_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__str_table_test:

-- =========================================================== sp__str_unpattern
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__str_unpattern',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120528
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__str_unpattern') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__str_unpattern') with nowait
        goto skip_sp__str_unpattern
        end
    if @ver>120528
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__str_unpattern') with nowait
        goto skip_sp__str_unpattern
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__str_unpattern') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__str_unpattern'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__str_unpattern]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120528\s.zaglio:wrapper for fn__str_unpattern
*/
CREATE proc sp__str_unpattern
    @blob ntext     =null,
    @filter sysname =null,
    @opt sysname   =null,
    @dbg int        =0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @blob is null goto help

select * from fn__str_unpattern(@blob,@filter,@opt)

goto ret

-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    wrapper for fn__str_unpattern, extract not repetitive words

Parameters
    @blob       the big text
    @filter     value for option
    @opt        options
                like    keep only lines the like @filter
                unlike  exclude lines that not like @filter

Examples
    sp__str_unpattern ''''
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 101
        error(-811561914) "local object "FN_GET_2OF5_CHECKDIGIT" is different" in "sp__script_alias"
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 102
        error(-811561914) "local object "FN_GET_3OF9_CHECKDIGIT" is different" in "sp__script_alias"
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 103
        error(-811561914) "local object "FN_GET_ID_ENTITY_FROM_CD_ERP_SHIPMENT" is different" in "sp__script_alias"
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 103
        error(-811561914) "local object "SP_MANAGE_MAIL_QUEUE" is different" in "sp__script_alias"
        Messaggio 50000, livello 16, stato 1, procedura sp__err, riga 103
        error(-811561914) "local object "SP_REFRESH_VIEWS" is different" in "sp__script_alias"
        '''',''''[0-9]%'''',''''unlike'''' -- excludes 101 and 102
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__str_unpattern'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__str_unpattern:

-- =================================================================== sp__style
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__style',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130909.0901
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__style') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__style') with nowait
        goto skip_sp__style
        end
    if @ver>130909.0901
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__style') with nowait
        goto skip_sp__style
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__style') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__style'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__style]

begin try
exec dbo.sp_executesql @statement = N'/*  Keep this due MS compatibility
    l:see LICENSE file
    g:utility
    v:130909.0901,130906\s.zaglio: better help;added shortcut into
    v:130707\s.zaglio: added a and j tag
    v:130416\s.zaglio: added more help
    v:130127\s.zaglio: added tag o and b
    v:130107\s.zaglio: added lower/upper case in setup option
    v:121202\s.zaglio: optimized
    v:121118.1900\s.zaglio: added object based template
    v:121118\s.zaglio: changed proc style to support exceptions
    v:121115\s.zaglio: added AC
    v:121031\s.zaglio: section #tbls init
    v:121029\s.zaglio: test mode
    v:120920\s.zaglio: added setup
    v:120918\s.zaglio: adapted to new script_template
    v:120906\s.zaglio: added unpvt and procie
    v:120831.1500\s.zaglio: improve of PROC
    v:120823\s.zaglio: added func
    v:120724\s.zaglio: added cursor template
    r:120625\s.zaglio: a total remake
    t:sp__style ''proc#SP_TEST'',@opt=''select''
    t:sp__style ''setup'' -- sp__style ''procie''
    t:sp__style ''func#test''
    t:sp__style ''h'' -- sp__style ''header''
*/
CREATE proc [dbo].[sp__style]
    @params sql_variant=null,
    @opt    sysname=null,
    @dbg    smallint=null
as
begin
set nocount on
declare
    @proc sysname,      -- for sp__trace
    @ret int,           -- standard API: 0=OK else STATUS(negative if failed)
    @err int            -- user for pure sql statements

select
    @proc=object_name(@@procid),@ret=0,@err=0,
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|''),
    @dbg=isnull(@dbg,0)

declare
    @p1 sysname,
    @p2 sysname,
    @p3 sysname,
    @p4 sysname,
    @psep char(1),
    @procbody sysname,
    @excludes sysname,
    @dt datetime

create table #tpi(
    name sysname,
    params sysname,
    description sysname,
    section sysname null
    )

create table #src (lno int identity primary key,line nvarchar(4000))
create table #vars (id nvarchar(16),value sql_variant)
create table #tpl (lno int identity primary key,line nvarchar(4000))
create table #tpl_sec(lno int identity,section sysname,line nvarchar(4000))
create table #tpl_cpl(
                    tpl binary(20),section sysname,y1 int,y2 int,
                    constraint pk_tpl_cpl primary key (tpl,section)
                )

-- ===================================================================== init ==
if @dbg>0 exec sp__elapsed @dt out,''-- begin''

select
    @psep=''#''
--                 name     params  description                         section
insert #tpi select ''proc'',  ''name'', ''stored procedure'',                 null
insert #tpi select ''prec'',  ''name'', ''stored procedure without except.'', null
insert #tpi select ''procwnt'',''name'', ''stored proc. with nested trans.'', ''proc''
insert #tpi select ''procio'',''name'', ''sp for I/O'',                       ''proc''
insert #tpi select ''procie'',''name'', ''sp for Import/Export'',             ''proc''
insert #tpi select ''cs'',    ''name'', ''cursor'',                           null
insert #tpi select ''func'',  ''name'', ''function'',                         null
insert #tpi select ''h'',     '''',     ''header'',                           ''header''
insert #tpi select ''header'','''',     ''header'',                           null
insert #tpi select ''unpvt'', '''',     ''unpivot table code'',               ''unpivot''
insert #tpi select ''setup'', ''grp'',  ''sp for setup of group'',            null
insert #tpi select ''ac'',    ''tbl'',  ''script alter constraint'',          null

if @params is null goto help

-- ###############################################################  templates ##

exec sp__Script_template ''
%proc%:
/*  leave this
    l:see LICENSE file
    g:[%groups%]
    k:[%keywords%]
    r:%builid%: short comment
    t:one line test
    t:
        multi line test
*/
create proc %proc_name%
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on
set xact_abort on                                                      --|ntrn|

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''''''),
    @opt=case when @opt is null then ''''||'''' else dbo.fn__str_quote(@opt,''''|'''') end
    -- @param=nullif(@param,''''''''),

-- nested transaction management                                       --|ntrn|
declare @trancount int                                                 --|ntrn|
select @trancount = @@trancount                                        --|ntrn|
if @trancount = 0                                                      --|ntrn|
    begin transaction                                                  --|ntrn|
else                                                                   --|ntrn|
    save transaction %proc_name%                                       --|ntrn|
                                                                       --|ntrn|
-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''''|sel|'''',@opt),@print=charindex(''''|print|'''',@opt),
    @run=charindex(''''|run|'''',@opt)|dbo.fn__isjob(@@spid)
        |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if  @run=0 goto help

/*  test if is it the test or development environment and               --|body|
    if not specified TEST option give an error because the sp can       --|body|
    steal data from production                                          --|body|
if dbo.fn__config(''''%app_test_code%'''','''') in (''''test'''',''''dev'''')         --|body|
and charindex(''''|test|'''',@opt)=0                                        --|body|
    begin                                                               --|body|
    raiserror(''''to run in test/dev env. need TEST option'''',16,1)          --|body|
    goto err                                                            --|body|
    end                                                                 --|body|
*/                                                                      --|body|

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

%proc_body%

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''''
Scope
    [write here a short desc]

Parameters
    [param]     [desc]
    @opt        options
    @dbg        debug level
                1   basic info and do not execute dynamic sql
                2   more details (usually internal tables) and execute dsql
                3   basic info, execute dsql and show remote info

Examples
    [example]
''''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"
                                                                       --|ntrn|
declare @xstate int                                                    --|ntrn|
select @xstate = xact_state();                                         --|ntrn|
if @xstate = -1                                                        --|ntrn|
    rollback;                                                          --|ntrn|
if @xstate = 1 and @trancount = 0                                      --|ntrn|
    rollback                                                           --|ntrn|
if @xstate = 1 and @trancount > 0                                      --|ntrn|
    rollback transaction usp_my_procedure_name;                        --|ntrn|

exec @ret=sp__err @cod=@proc,@opt=''''ex''''
return @ret
end catch   -- proc %proc_name%
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%prec%:
/*  leave this
    l:see LICENSE file
    g:[%groups%]
    k:[%keywords%]
    r:%builid%: short comment
    t:one line test
    t:
        multi line test
*/
create proc %proc_name%
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp  (not correct if called by remote sp)

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''''''),''''|'''')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @opt1 bit,@opt2 bit,
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @opt1=charindex(''''|opt|'''',@opt),
    @end_declare=1

-- ======================================================== second params chk ==
if @opt=''''||'''' -- charindex(''''|run|'''',@opt)=0
and dbo.fn__isjob(@@spid)=0      -- if can run from a job               --|body|
    goto help

/*  test if is it the test or development environment and               --|body|
    if not specified TEST option give an error because the sp can       --|body|
    steal data from production                                          --|body|
if dbo.fn__config(''''%app_test_code%'''','''') in (''''test'''',''''dev'''')         --|body|
and charindex(''''|test|'''',@opt)=0                                        --|body|
    begin                                                               --|body|
    select @e_msg=''''to run in test/dev env. need TEST option''''          --|body|
    goto err                                                            --|body|
    end                                                                 --|body|
*/                                                                      --|body|

-- =============================================================== #tbls init ==

-- ===================================================================== body ==

%proc_body%

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
/*
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_me1:    select @e_msg=''''write here msg''''                              goto err
err_me2:    select @e_msg=''''write this %s'''',@e_p1=@var                    goto err
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''''
Scope
    [write here a short desc]

Parameters
    [param]     [desc]

Examples
    [example]
''''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc %proc_name%
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%header%:
/*  leave this
    l:see LICENSE file
    g:[%groups%]
    k:[%keywords%]
    r:%builid%: short comment
    t:one line test
*/
create
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%cursor%:
declare %cs% cursor local for
    select %%flds%%
    from %%tbl%%
    where 1=1
open %cs%
while 1=1
    begin
    fetch next from %cs% into %%vars%%
    if @@fetch_status!=0 break
    end -- cursor %cs%
close %cs%
deallocate %cs%
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%func%:
/*  leave this
    l:see LICENSE file
    g:[%groups%]
    k:[%keywords%]
    r:%builid%: short comment
    t:one line test
*/
create function %name%(
    -- @opt sysname = null,
    -- @dbg int=0
    )
-- returns type
-- returns table @t(id int identity,...)
-- returns table as select ...
as
begin
declare @ret type
select @ret=
return @ret
end -- %name%
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%body%:
-- ...
-- if ??? goto err_sample
-- ...
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%bodyio%:
-- 1. input 1 row of data by params
-- 2. input 1 or more rows of data by #tbl of same structure os returned rs
-- 3. set command for: list, delete (ins, upd are deduced)
-- 4. load data into internal normalized tables
-- 5. ins/upd/del internal tables
-- 6. upd/ins/del storage with internal tables
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%bodyie%:
-- 1. import remote files and keep a local copy
-- 2. process files into #temp
-- 3. create middle tables and find correct PKey
   3.1 the name of source file cannot be part of the PK
   3.2 if the source file has a progressive, set regression as errors
-- 4. do other control
-- 5. send reports to technichan
-- 6. copy existing in a flat history file
-- 7. ins/upd new records on PK in a middle table
-- 8. eventually update remote files info renaming in .OK or .ERR
-- 9. reupdate middle data with codes associations
-- 10. ins/upd final data with last updates
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%unpivot%:
select
    col,
    val
from
    (
    select
        cast(ca as sql_variant) as ca,
        cast(cb as sql_variant) as cb
    from (
        select 1 as ca, 2 as cb
        union all
        select 3 as ca, 4 as cb
        ) my_table
    ) as t
    unpivot
    (
    val
    for col in (ca, cb)
    ) as unpvt
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%setup%:
/*  leave this
    l:see LICENSE file
    g:[%groups%]
    k:[%keywords%]
    r:%builid%: setup objects for group %grp%
    t:%grp%_setup @opt=''''run''''
*/
create proc %grp_ssp%
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp  (not correct if called by remote sp)
declare
    @proc sysname, @err int, @ret int -- @ret: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''''''),''''|'''')
-- ========================================================= param formal chk ==
if charindex(''''|run|'''',@opt)=0 and dbo.fn__isjob(@@spid)=0 goto help

-- ============================================================== declaration ==
declare
    @util_ver sysname

-- =========================================================== initialization ==
select
    -- select dbo.fn__group_version(''''utility'''')
    @util_ver=dbo.fn__group_version(''''utility'''')

-- ======================================================== second params chk ==
if @util_ver<''''%utility_ver%'''' goto err_ver

-- ===================================================================== body ==

exec sp__printf ''''-- %s: utility version is:%d'''',@proc,@util_ver

-- ============================================= add base data if 1st install ==
-- if not exists(select * from %table% where ...)
-- if and not object_id(%sp%) is null exec ...

-- sp__printf''''do others''''

-- ============================================================== install job ==
/*
-- sp_setup_job get app name, smtp and support emails and call "sp__job"
if not object_id(''''sp_setup_job'''') is null    -- local application job initializer
    exec sp_setup_job ''''%action%'''',''''sp_%action%'''',''''%at%'''' --,@grp=''''%grp%''''
*/
-- ================================================================== dispose ==

dispose:if @@trancount>0 rollback
goto ret

-- =================================================================== errors ==
err_ver:
exec @ret=sp__err ''''please update to latest utility version'''',@proc
goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''''
Scope
    called before execute group script and after, create table and populate data

Notes
    this sp must be reentrant because is called before script where tables and
    stored do not exists and after and a second time again;
    so must use exists and object_id to test presence of data and objects

Parameters
    @opt    options
            run     to execute it (run automatically if executed from job)

Examples

''''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc %grp_ssp%

-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%ac%:
alter table [%tbl%] drop constraint %constraint%
alter table [%tbl%]
    add constraint %constraint% default (%val%) for %column%
''

-- ###############################################################  templates ##

if @dbg>0 exec sp__elapsed @dt out,''-- after templates''

-- ===================================================================== body ==

select @p1=dbo.fn__str_at(convert(sysname,@params),@psep,1)
select @p2=dbo.fn__str_at(convert(sysname,@params),@psep,2)
select @p3=dbo.fn__str_at(convert(sysname,@params),@psep,3)
select @p4=dbo.fn__str_at(convert(sysname,@params),@psep,4)

if @dbg>0 exec sp__printf ''-- 1:%s, 2:%s, 3:%s, 4:%s'',@p1,@p2,@p3,@p4

if not object_id(@p1) is null
    begin
    -- template base on similar object, excluding body section
    exec sp__script @p1             -- script the obj to #src
    update #src set line=replace(line,@p1,isnull(nullif(@p2,''''),''%new_name%''))
    delete from #src
    where lno>
        (select lno from #src where line like ''%= body =%'')
    and  lno<
        (select lno from #src where line like ''%= dispose =%'')
    exec sp__print_table ''#src''
    goto ret
    end -- object based template


-- common macro
insert #vars
select ''%builid%'',convert(sysname,getdate(),12)+''\%''+system_user+''%''

if @p1=''setup''
    insert #vars
    select ''%utility_ver%'',dbo.fn__group_version(''utility'')

-- alter here info for common sections
select @procbody=case @p1
                 when ''procio'' then ''%bodyio%''
                 when ''procie'' then ''%bodyie%''
                 else ''%body%''
                 end
select @excludes=replace(@procbody,''%'','''')
if @p1!=''procwnt'' select @excludes=@excludes+''|ntrn''

-- get main section
select @p1=isnull(section,name) from #tpi where name=@p1

if @dbg>0 exec sp__elapsed @dt out,''-- after inits''

-- main sections process
if @p1=''proc''
    begin
    if isnull(@p2,'''')='''' select @p2=''not_specified''
    insert #vars select ''%proc_name%'',@p2
    exec sp__script_template @procbody,''%proc_body%'',@excludes=@excludes
    end -- proc

if @p1=''prec''
    begin
    if isnull(@p2,'''')='''' select @p2=''not_specified''
    insert #vars select ''%proc_name%'',@p2
    exec sp__script_template @procbody,''%proc_body%'',@excludes=@excludes
    end -- prec

if @p1=''func''
    begin
    if isnull(@p2,'''')='''' select @p2=''not_specified''
    insert #vars select ''%name%'',@p2
    exec sp__script_template ''%bodyio%'',''%body%'',@excludes=@excludes
    end -- proc

if @p1=''cs''
    begin
    select @p1=''cursor''
    if isnull(@p2,'''')='''' select @p2=''cs''
    insert #vars select ''%cs%'',@p2
    end -- proc

if @p1=''setup''
    begin
    -- sp__style ''setup#test''
    -- sp__style ''setup#TEST''
    if isnull(@p2,'''')='''' select @p2=''%grp%''
    insert #vars select ''%grp%'',@p2
    if @p2=lower(@p2) collate SQL_Latin1_General_CP850_BIN
        select @p2=@p2+''_setup''
    else
        select @p2=@p2+''_SETUP''
    insert #vars select ''%grp_ssp%'',@p2
    end -- proc

if @p1=''ac''
    begin
    -- sp__style ''ac#tbl''
    if isnull(@p2,'''')='''' select @p2=''%tbl%''
    insert #vars select ''%tbl%'',@p2
    end -- ac

select @p1=''%''+@p1+''%''

if @dbg>0 exec sp__elapsed @dt out,''-- after templating''

exec sp__script_template @p1,@excludes=@excludes

if @dbg>0 exec sp__elapsed @dt out,''-- after mixing''

if charindex(''|select|'',@opt)>0
    select line from #src order by lno
else
    exec sp__print_table ''#src''

if @dbg>0 exec sp__elapsed @dt out,''-- after print''

goto ret

/* =============================== errors ================================= */
err:        -- init of error management

/* ================================ help ================================== */
help:
-- sp__style
exec sp__usage @proc,@extra=''
Comment tag styles
    v:one line short Version comment
    r:short Release comment
    t:single test line
    t:
      --example of multiple
      exec sp__printf ''''test line''''
    c:single or multile Comment line
    g:%group1%[,%group2%[,...]]
    s:See also,...
    d:deprecated target function (see notes)
    o:obsolete tag means that this object replaces the obj_name (see notes)
    k:%keywords%
    a:%alias% tag compile the object into a synonym of %alias%(if exists)
    j:reserved for future use for jobs
    b:%buildin% (not with common know mean) that align the date-version system
      to application/platform versioning system (see notes)
    p:profile

Press  F6  CTRL+A  CTRL+C  F6  CTRL+A  CTRL+V to copy & paste result
*** this is the better way (the 4th experimented in 3 years of develop),
in mssql200x, to develop test and debug tsql code

Notes
    * I normally associate this sp to shortcut CTRL+8
    * Tag V,R
      v:yymmdd[.hhmm][,old_ver]\author: comment[;comment of old_ver]
      Examples
        v:121104\s.zaglio: this is a tipical comment
        v:121103,121102\s.zaglio: this is the 121103 comment; this of 121102
    * Tag K
      On tag "k" do not repeat groups; do not use more that 4,5 keywords;
      do not use implicit or deductible words.
    * Tag D
      d:yymmdd\u.name:obj_name
                will delete obj_name on next sp__script_group
                The lst obj_name "V" tag can contain
                the motivation
    * Tag O
      o:yymmdd\u.name:obsolete_obj_name
                when an object is still used, cannot be deprecated so temporarily
                is marked as obsolete from current object; in the future will
                change to D tag
    * Tag b
      b:ver.rel.builin|modifiers
                can be used by an application/platform utility to place inform
                the sp__script_group to place the script into a specific place
    * Tag a
      see for example fn__hex that is a duplicate of new ms sys.fn_varbintohexstr
      or fn__str_table_fast that is a duplicate of fn__str_split

    * Tag p
      specilize the profile (or target or customer)


List of templates
''
exec sp__select_astext ''#tpi'',@header=1
select @ret=-1    -- generic Help error

ret:     -- procedure end...is better than return
return @ret
end -- sp__style'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__style:

-- ===================================================================== sp__svn
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__svn',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090910
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__svn') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__svn') with nowait
        goto skip_sp__svn
        end
    if @ver>090910
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__svn') with nowait
        goto skip_sp__svn
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__svn') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__svn'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__svn]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090910\s.zaglio: added output of all script of cur db to svn
    r:090302\S.Zaglio: interface bewteen mssql and svn
*/
CREATE proc [dbo].[sp__svn] @obj sysname,@svr sysname,@uid sysname,@pwd sysname
as
begin
/*
xp_cmdshell ''svn  help list''
xp_cmdshell ''svn ls svn://lupin/SOURCES/ -r HEAD''
-- sp__find ''svn://lupin/SOURCES''  -- list content recursivelly
-- sp__find ''svn://lupin/SOURCES/\*.vb'' -- set and do a download (populate system table fnd)
-- sp__find -- list active downloads
xp_cmdshell ''svn ls svn://lupin/SOURCES/SINTESI/trunk -r HEAD -R -v'' --username stefano --password prova''
xp_cmdshell ''svn ls svn://lupin/SOURCES/SINTESI/trunk -r HEAD -R --xml'' --username stefano --password prova''

  71106 davide                set 16 09:46 PDA/Build/
  68797 giovanni              mag 19  2011 PDA/Build/Argos/
  68594 giovanni         1631 mag 06  2011 PDA/Build/Argos/config.xml

  Con --verbose i seguenti campi verranno mostrati per ogni elemento:
    Numero di revisione dell''ultimo commit
    Autore dell''ultimo commit
    Se bloccato, la lettera ''O''. (Usa ''svn info URL'' per i dettali)
    Dimensioni (in byte)
    Data e ora dell''ultimo commit

 sp__script ''sp__script'',@out=''src__sp_script.sql'',@dbg=1    -- out to %temp%\...sql
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe help'',@dbg=1 -- ???
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe help update'',@dbg=1 -- ???

 sp__run_cmd ''dir /a "c:\Documents and Settings\all users\desktop" /s''

 -- brutal remove
 sp__run_cmd ''rmdir /q /s "c:\Documents and Settings\all users\desktop\ramses"''

 -- checkout
xp_cmdshell ''svn checkout svn://lupin/SOURCES/SINTESI/ "%temp%\sp__find" -r HEAD --depth infinity ''--username stefano --password prova''
xp_cmdshell ''svn help checkout''
xp_cmdshell ''dir /s %temp%\sp__find''
xp_cmdshell ''rmdir /q /s %temp%\sp__find''

 -- cleanup
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe cleanup "c:\Documents and Settings\all users\desktop\ramses\sviluppo\db"''

 -- test existance
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe ls svn://gamon/PROJECTS/RAMSES/Sviluppo/DB/readme.txt'',@dbg=1 -- ???
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe ls svn://gamon/PROJECTS/RAMSES/Sviluppo/DB/this_dont_exist.txt'',@dbg=1 -- ???

 -- add new that already exists
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe add "c:\Documents and Settings\all users\desktop\ramses\sviluppo\db\readme.txt"'',@dbg=1 -- ???
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe add "c:\Documents and Settings\all users\desktop\ramses\sviluppo\db\i_dont_exist.txt"'',@dbg=1 -- ???

 -- try add new
 sp__run_cmd ''echo test from sp__svn>"c:\Documents and Settings\all users\desktop\ramses\sviluppo\db\test.txt"''
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe add "c:\Documents and Settings\all users\desktop\ramses\sviluppo\db\test.txt"'',@dbg=1 -- ???
 sp__run_cmd ''c:\programmi\svn\bin\svn.exe commit "c:\Documents and Settings\all users\desktop\ramses\sviluppo\db" -m "one commit test"''
 sp__run_cmd ''echo test change from sp__svn>>"c:\Documents and Settings\all users\desktop\ramses\sviluppo\db\test.txt"''
 -- do again add and commit

 sp__run_cmd ''c:\programmi\svn\bin\svn.exe update "c:\Documents and Settings\all users\desktop\ramses\sviluppo\db"''

 -- to see the user
 sp__run_cmd ''set''
*/
print ''to do''
goto ret
ret:
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__svn:

-- ========================================================= sp__sysobjects_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__sysobjects_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131002
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__sysobjects_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__sysobjects_test') with nowait
        goto skip_sp__sysobjects_test
        end
    if @ver>131002
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__sysobjects_test') with nowait
        goto skip_sp__sysobjects_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__sysobjects_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__sysobjects_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__sysobjects_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:fn__script_drop,fn__sysobjects
    v:131002,131001\s.zaglio: test for fn__script_drop.fn__sysobjects
    t:sp__sysobjects_test @opt=''run''
*/
CREATE proc sp__sysobjects_test
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    @row_id int,@drop_script nvarchar(512),@if_exists nvarchar(512),
    @sql nvarchar(max),@run bit
-- =========================================================== initialization ==
select @run=charindex(''|run|'',@opt)
if @run=0 goto help
-- ======================================================== second params chk ==
-- =============================================================== #tbls init ==
-- ===================================================================== body ==
-- drop table #tmp
select
    identity(int,1,1) row_id,
    *,
    cast(null as nvarchar(2000)) drop_script_status,
    cast(null as nvarchar(2000)) if_exists_status
into #tmp
-- select *
from fn__sysobjects(default,default,''drop_script|if_exists'')
where obj!=''log_ddl''                    -- give error in sp__script_store
-- where drop_Script like ''%fn__ntext%''
order by obj,typ

update #tmp set if_exists=replace(replace(if_exists,char(13),'' ''),char(10),'' '')
while exists(select top 1 null from #tmp where charindex(''  '',if_exists)>0)
    update #tmp set if_exists=replace(if_exists,''  '','' '')
while exists(select top 1 null from #tmp where charindex('' )'',if_exists)>0)
    update #tmp set if_exists=replace(if_exists,'' )'','')'')
while exists(select top 1 null from #tmp where charindex(''( '',if_exists)>0)
    update #tmp set if_exists=replace(if_exists,''( '',''('')

exec sp__script_store @opt=''dis'' -- disable

declare cs cursor local for
    select row_id,drop_Script,if_exists
    from #tmp
    -- select * from fn__sysobjects(default,default,''drop_script|if_exists'')
    where 1=1 -- and obj=''fn__ntext_to_lines''
    and not if_exists is null
    and not drop_script is null
    and not typ in (''D''/*constraint*/,
                    ''SQ''/*service queue*/,
                    ''IT''/*internal table*/,
                    ''FS''/*clr fn*/)
    and not isnull(parent_typ,'''') in (''IT'')
    and not (typ=''PK'' and parent_typ!=''U'')
open cs
while 1=1
    begin
    fetch next from cs into @row_id,@drop_script,@if_exists
    if @@fetch_status!=0 break

    -- test if exists
    select @sql=''
        select @err_msg=null
        begin tran savepoint
        begin try
        ''+@if_exists+'' select @err_msg=''''ok'''' else select @err_msg=''''ko''''
        rollback
        end try
        begin catch
        select @err_msg=''''ko:''''+error_message()+''''(''''+error_procedure()+'''')''''
        if @@trancount>0 rollback tran savepoint
        end catch''

    exec sp_executesql @sql,N''@err_msg nvarchar(2000) out'',@err_msg=@err_msg out

    if @err_msg is null raiserror(''inside error'',16,1)
    -- if @err_msg=''ko'' exec sp__printsql @sql

    update #tmp set if_exists_status=@err_msg where row_id=@row_id

    -- test drop
    select @sql=''
        select @err_msg=null
        begin tran savepoint
        begin try
        ''+@drop_script+''
        select @err_msg=''''ok''''
        rollback
        end try
        begin catch
        select @err_msg=''''ko:''''+error_message()+''''(''''+error_procedure()+'''')''''
        if @@trancount>0 rollback tran savepoint
        end catch''

    exec sp_executesql @sql,N''@err_msg nvarchar(2000) out'',@err_msg=@err_msg out
    -- if @err_msg!=''ok'' exec sp__printsql @sql

    if @err_msg is null raiserror(''inside error'',16,1)

    update #tmp set drop_script_status=@err_msg where row_id=@row_id

    end -- cursor cs
close cs
deallocate cs

select @sql=''
    select
        drop_script_status,if_exists_status,sch,obj,typ,[drop],
        parent,parent_typ,drop_script,if_exists
        from #tmp
        order by drop_script_status,if_exists_status,obj,typ
    ''
-- exec(@sql)
exec sp__select_astext @sql

exec sp__Script_store @opt=''ena'' -- enable

if exists(select top 1 null from #tmp where left(drop_script_status,2)=''ko'')
    raiserror(''test failed'',16,1)

-- ================================================================== dispose ==
dispose:
drop table #tmp

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test fn__sysobjects and indirectly fn__script_drop
    also executing the scripts (into transactions with rollback)

Notes
    ### this sp drop all objects into a transaction that will be rollbacked ###

Parameters
    [param]     [desc]
    @opt        options
                run     execute the test
    @dbg        (not used)

Examples
    sp__sysobjects_test @opt="run"
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"
exec sp__Script_store @opt=''ena'' -- disable
exec sp__printsql @sql
exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__sysobjects_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__sysobjects_test:

-- ============================================================ sp__table_2_html
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__table_2_html',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130123
        begin
        if @aut!='L.Mazzanti'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__table_2_html') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__table_2_html') with nowait
        goto skip_sp__table_2_html
        end
    if @ver>130123
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__table_2_html') with nowait
        goto skip_sp__table_2_html
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__table_2_html') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__table_2_html'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__table_2_html]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    r:130123\L.Mazzanti: stampa in output il testo html di una tabella
    t: esempio

        declare @HTML nvarchar(max)
        set @HTML=''''

        EXEC SpCustomTable2HTML
            #tmp,
            @HTML OUTPUT,
            ''style="font:8pt" class="AltListBorder" cellpadding="2" cellspacing="1"'',
            ''class="RowHeader"''

        PRINT @HTML
*/
CREATE PROCEDURE [dbo].[sp__table_2_html] (
    @TABLENAME  NVARCHAR(500),
    @OUTPUT   NVARCHAR(MAX) OUTPUT,
    @TBL_STYLE NVARCHAR(1024) = '''',
    @HDR_STYLE NVARCHAR(1024) = '''')
AS
BEGIN
    -- @exec_str stores the dynamic SQL Query
    -- @ParmDefinition stores the parameter definition for the dynamic SQL
    DECLARE @exec_str  NVARCHAR(MAX)
    DECLARE @ParmDefinition NVARCHAR(500)


    --We need to use Dynamic SQL at this point so we can expand the input table name parameter
    SET @exec_str= N''
    DECLARE @exec_str  NVARCHAR(MAX)
    DECLARE @ParmDefinition NVARCHAR(500)

    --Make a copy of the original table adding an indexing columnWe need to add an index column to the table to facilitate sorting so we can maintain the
    --original table order as we iterate through adding HTML tags to the table fields.
    --New column called CustColHTML_ID (unlikely to be used by someone else!)
    --

    select CustColHTML_ID=0,* INTO #CustomTable2HTML FROM '' + @TABLENAME + ''

    --Now alter the table to add the auto-incrementing index. This will facilitate row finding
    DECLARE @COUNTER INT
    SET @COUNTER=0
    UPDATE #CustomTable2HTML SET @COUNTER = CustColHTML_ID=@COUNTER+1

    -- @HTMLROWS will store all the rows in HTML format
    -- @ROW will store each HTML row as fields on each row are iterated through
    -- using dymamic SQL and a cursor
    -- @FIELDS will store the header row for the HTML Table

    DECLARE @HTMLROWS NVARCHAR(MAX) DECLARE @FIELDS NVARCHAR(MAX)
    SET @HTMLROWS='''''''' DECLARE @ROW NVARCHAR(MAX)

    -- Create the first HTML row for the table (the table header). Ignore our indexing column!
    SET @FIELDS=''''<tr '' + @HDR_STYLE + ''>''''
    SELECT @FIELDS=COALESCE(@FIELDS, '''' '''','''''''')+''''<td>'''' + name + ''''</td>''''
    FROM tempdb.sys.Columns
    WHERE object_id=object_id(''''tempdb..#CustomTable2HTML'''')
    AND name not like ''''CustColHTML_ID''''
    SET @FIELDS=@FIELDS + ''''</tr>''''

    -- @ColumnName stores the column name as found by the table cursor
    -- @maxrows is a count of the rows in the table, and @rownum is for marking the
    -- ''''current'''' row whilst processing

    DECLARE @ColumnName  NVARCHAR(500)
    DECLARE @maxrows INT
    DECLARE @rownum INT

    --Find row count of our temporary table
    SELECT @maxrows=count(*) FROM  #CustomTable2HTML


    --Create a cursor which will look through all the column names specified in the temporary table
    --but exclude the index column we added (CustColHTML_ID)
    DECLARE col CURSOR FOR
    SELECT name FROM tempdb.sys.Columns
    WHERE object_id=object_id(''''tempdb..#CustomTable2HTML'''')
    AND name not like ''''CustColHTML_ID''''
    ORDER BY column_id ASC

    --For each row, generate dymanic SQL which requests the each column name in turn by
    --iterating through a cursor
    SET @rowNum=0
    SET @ParmDefinition=N''''@ROWOUT NVARCHAR(MAX) OUTPUT,@rowNum_IN INT''''

    While @rowNum < @maxrows
    BEGIN
      SET @HTMLROWS=@HTMLROWS + ''''<tr>''''

      SET @rowNum=@rowNum +1
      OPEN col
      FETCH NEXT FROM col INTO @ColumnName
      WHILE @@FETCH_STATUS=0
        BEGIN
          --Get nth row from table
          --SET @exec_str=''''SELECT @ROWOUT=(select top 1 ['''' + @ColumnName + ''''] from (select top '''' + cast(@rownum as varchar) + '''' * from #CustomTable2HTML order by CustColHTML_ID ASC) xxx order by CustColHTML_ID DESC)''''
          SET @exec_str=''''SELECT @ROWOUT=(select ISNULL(['''' + @ColumnName + ''''],'''''''''''''''') AS [''''+@ColumnName+''''] from #CustomTable2HTML where CustColHTML_ID=@rowNum_IN)''''

          EXEC    sp_executesql
                @exec_str,
                @ParmDefinition,
                @ROWOUT=@ROW OUTPUT,
                @rowNum_IN=@rownum

          SET @HTMLROWS =@HTMLROWS +  ''''<td>'''' + @ROW + ''''</td>''''
          FETCH NEXT FROM col INTO @ColumnName
        END
      CLOSE col
      SET @HTMLROWS=@HTMLROWS + ''''</tr>''''
    END

    SET @OUTPUT=''''''''
    IF @maxrows>0
    SET @OUTPUT= ''''<table '' + @TBL_STYLE + ''>'''' + @FIELDS + @HTMLROWS + ''''</table>''''

    DEALLOCATE col
    ''

    DECLARE @ParamDefinition nvarchar(max)
    SET @ParamDefinition=N''@OUTPUT NVARCHAR(MAX) OUTPUT''

    --Execute Dynamic SQL. HTML table is stored in @OUTPUT which is passed back up (as it''s
    --a parameter to this SP)
    -- print @exec_str
    EXEC sp_executesql @exec_str,
    @ParamDefinition,
    @OUTPUT=@OUTPUT OUTPUT

    RETURN 1
END'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__table_2_html:

-- ==================================================================== sp__test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130609
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__test') with nowait
        goto skip_sp__test
        end
    if @ver>130609
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__test') with nowait
        goto skip_sp__test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,test
    k:test,sp,case
    v:130609\s.zaglio: done
    r:130608\s.zaglio: create case tests
    t:sp__test ''sp__test'','''',''sp__test'',@dbg=1  -- insert/update unnamed/default
    t:sp__test ''sp__test'',''.'',''sp__test''        -- error
    t:sp__test ''sp__test'',''test1'',''sp__test @dbg=1'',@dbg=1 -- insert named
    t:sp__test ''sp__test'',@opt=''list'' -- exec sp__test ''0x80000027'',@opt=''sel''
    t:sp__test ''sp__test'',@opt=''list|sel''
    t:sp__test ''sp__test'',@dbg=1 -- test (differ because change the last list
    t:sp__test ''sp__test'',@name=''%''
    t:sp__test ''0x00000002'',@opt=''status''
    t:sp__test ''0x00000001'',@opt=''source''
    t:sp__test ''0x00000001'',@opt=''result''
    t:sp__test ''0x00000002'',@opt=''del''              -- del test
    t:sp__test ''sp__test_demo'',@opt=''del''           -- purge test
    t:sp__test ''0x00000001'',@opt=''source|result''    -- error
    t:truncate table tst
*/
CREATE proc sp__test
    @obj sysname = null,
    @name sysname = null,
    @tst nvarchar(4000) = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp
-- set forceplan off -- (**)
set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @i int,@n int,                         -- index, counter
    @d datetime,
    @tmp nvarchar(max),                    -- dynamic sql
    -- options
    @opt_list bit,
    @opt_del bit,
    @opt_sel bit,
    @opt_testing bit,
    @opt_quiet bit,
    @opt_source bit,
    @opt_result bit,
    @opt_status bit,

    -- summary conditions
    @opt_detail bit,
    @purge bit,
    @disappeared bit,
    @testing bit,

    @code nvarchar(max),
    @result nvarchar(max),
    @status nvarchar(max),
    @crlf nvarchar(2),
    @non_word sysname,
    @non_sentence sysname,
    @id int,
    @rid int,
    @cmd nvarchar(1024),
    @err_test sysname,
    @err_osql sysname,
    @file nvarchar(1024),
    @msg nvarchar(1024),

    -- statuses
    @sts_untested nchar(2),
    @sts_passed nchar(2),
    @sts_error nchar(2),
    @sts_disappeared nchar(2),
    @sts_failed nchar(2),

    @end_declare bit

declare @out table (lno int identity primary key, line nvarchar(4000))

-- =========================================================== initialization ==
select
    -- constants
    @crlf=crlf,
    @rid=power(-2,31),
    @err_test=''test failed'',
    @err_osql=''call of sqlcmd failed'',
    @non_word=non_word,
    @non_sentence=non_sentence,
    -- parameters normalization/adjust
    @obj=nullif(ltrim(rtrim(@obj)),''''),
    @id=case
        when left(isnull(@obj,''''),2)=''0x''
        then dbo.fn__hex2int(@obj)
        else null
        end,
    @name=case when ltrim(rtrim(@name))='''' then ''default'' else @name end,
    @tst=nullif(ltrim(rtrim(@tst)),''''),
    -- options
    @opt_list=charindex(''|list|'',@opt),
    @opt_sel=charindex(''|sel|'',@opt),
    @opt_quiet=charindex(''|quiet|'',@opt),
    @opt_del=charindex(''|del|'',@opt),
    @opt_source=charindex(''|source|'',@opt),
    @opt_result=charindex(''|result|'',@opt),
    @opt_status=charindex(''|status|'',@opt),
    @opt_detail=@opt_source|@opt_result|@opt_status,
    @purge=case when @opt_del=1 and left(@obj,2)!=''0x'' then 1 else 0 end,
    @testing=case when @tst is null then 1 else 0 end,
    -- other vars
    @file=''%temp%\tmp_''+replace(newid(),''-'',''_'')+''.txt'',
    -- end commodity
    @end_declare=1
from fn__sym() -- constant/symbol source

if not @id is null and @purge=0 select @obj=''%''

select
    -- statuses constants
    @sts_untested=''??'',
    @sts_passed=''ok'',
    @sts_error=''!#'',
    @sts_failed=''ko'',
    @sts_disappeared=''--''

if not @tst is null
and (patindex(@non_sentence,@name)>0 or patindex(@non_word,@obj)>0)
    raiserror(''wild char cannot be used when ins/upd a test'',16,1)

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1
if object_id(''tst'') is null
    begin
    -- drop table tst drop view tst_list
    exec(''
    create table tst(
        id int identity constraint pk_tst primary key,
        obj sysname not null,
        name sysname not null,
        code nvarchar(4000) not null,
        result nvarchar(max) not null,
        status nvarchar(max) not null,
        ins_dt datetime not null,
        last_run_dt datetime not null,
        first_run_ms bigint null,
        last_run_ms bigint null
        )
    create unique index ix_obj_name on tst(obj,name)
    '')
    end

-- ======================================================== second params chk ==

if cast(@opt_source as tinyint)+@opt_result+@opt_status+@opt_del>1
    raiserror(''source, result,status and del cannot be specified together'',16,1)

if (@opt_list=1 and not @obj is null)
or (not @id is null and (@opt_del|@opt_detail)=0)
    begin
    select
        dbo.fn__hex(id) id,
        obj,name,
        case
        when status=@sts_disappeared then ''disappeared''
        when status=@sts_untested then ''untested''
        when status=@sts_passed then ''passed''
        when status=@sts_error then ''error''
        else ''failed'' -- containt the two results
        end as [status],
        ins_dt,last_run_dt,last_run_ms,first_run_ms
    into #tmp
    from tst
    where obj like isnull(@obj,''%'') and name like isnull(@name,''%'')
    and (@id is null or id=@id)

    if @opt_sel=0 exec sp__select_astext ''select * from #tmp order by 2,3''
    else select * from #tmp order by 2,3

    drop table #tmp
    goto ret
    end

if not @obj is null goto ac_ins_upd_run

-- default action not managed, show help
goto help

-- =============================================================== #tbls init ==
ac_ins_upd_run:

-- ===================================================================== body ==

-- scan tests and apply actions specified by parameters or options
declare cs cursor local for
    select
        id,obj,name,code,result
    from tst
    where obj like @obj and name like isnull(@name,''%'')
    and (@id is null or id=@id)
    order by obj,name
open cs
while 1=1
    begin
    fetch next from cs into
        @id,@obj,@name,@code,@result

    if @@fetch_status!=0 and @testing=1 break

    select @disappeared=case when object_id(@obj) is null then 1 else 0 end

    -- if to delete or purge
    if @opt_del=1
        begin
        if @purge=1 and @disappeared=0
            raiserror(''cannot purge tests of existing objects'',16,1)
        delete from tst where id=@id
        if @@rowcount=0 raiserror(''not found'',16,1)
        exec sp__printf ''-- deleted "%x|%s|%s"'',@id,@obj,@name
        continue
        end

    -- list details of specific test
    if @opt_detail=1
        begin
        if @opt_source=1
            select @tmp=code from tst where id=@id
        if @opt_result=1
            select @tmp=result from tst where id=@id
        if @opt_status=1
            select @tmp=status from tst where id=@id
        if @opt_sel=0 exec sp__printsql @tmp
        else select line from fn__ntext_to_lines(@tmp,0)
        break -- only one test ha sense to show...
        end

    select @msg=''|''+left(dbo.fn__hex(@id)+''|''+@obj+''|''+@name
                     +replicate('' '',0),77)

    if @disappeared=1
        begin
        update tst set
            last_run_dt=getdate(),status=@sts_disappeared
        where id=@id
        select @msg=@sts_disappeared+@msg
        end
    else
        begin
        -- execute the test and store results
        select @cmd=''sqlcmd -W -u -E '' -- compact, unicode
                   +''-S "''+@@servername+''" ''
                   +''-d "''+db_name()+''" ''
                   +''-Q "''+isnull(@tst,@code)+''" ''
                   +''-o ''+@file

        select @tmp='''',@d=getdate()
        insert @out(line) exec @ret=xp_cmdshell @cmd
        select @n=datediff(ms,@d,getdate())
        if @dbg>0
            select lno,line
            from (
                select 0 lno,@cmd as line
                union
                select lno,line from @out
                ) o
            order by lno

        if @ret!=0 raiserror(@err_osql,16,1)

        exec sp__file_read_stream @file,@tmp out,@fmt=''unicode''

        -- delete temp file
        select @cmd=''del /q ''+@file
        exec @ret=xp_cmdshell @cmd,no_output

        if @tmp is null raiserror(@err_osql,16,1)
        if @dbg>0 exec sp__printsql @tmp

        if patindex(''%Messaggio %, livello %, stato %'',@tmp)>0
        or patindex(''%Message %, level %, state %'',@tmp)>0
        -- sqlcmd when errors happen
        or patindex(''HResult %, level %, state %'',@tmp)>0
        or patindex(''HResult %, livello %, stato %'',@tmp)>0
            select @status=@sts_error
        else
            select @status=case @testing
                           when 1 then @sts_passed else @sts_untested
                           end

        if not @id is null                  -- update test info
            begin

            if @testing=1 and @disappeared=0
                begin
                if @result=@tmp
                    select @msg=@status+@msg
                else
                    begin
                    if @status=@sts_error select @msg=@sts_error+@msg
                    else select @msg=@sts_failed+@msg
                    select
                        @status
                           =dbo.fn__prints(''8<resulted'',null,null,null,null)+@crlf
                           +@tmp+@crlf
                           +dbo.fn__prints(''8<expected'',null,null,null,null)+@crlf
                           +@result+@crlf
                           +dbo.fn__prints(''8<'',null,null,null,null)+@crlf
                    end
                end
            else
                select @msg=@status+@msg

            if @testing=1
                begin
                update tst set
                    status=@status,last_run_dt=getdate(),last_run_ms=@n
                where id=@id
                end
            else
                begin
                update tst set
                    code=isnull(@tst,@code),result=@tmp,status=@status,
                    last_run_dt=getdate(),last_run_ms=@n
                where id=@id
                exec sp__printf ''-- updated "%s"'',@name
                break; -- consider in the future a full update?
                end

            end -- update/run test

        else

            begin

            if @dbg>0 exec sp__printsql @result

            select @d=getdate(),@code=@tst,@result=@tmp
            insert tst(obj,name,code,result,status,ins_dt,last_run_dt,first_run_ms,last_run_ms)
            select @obj,@name,@code,@result,@status,@d,@d,@n,@n

            exec sp__printf ''-- inserted "%s"'',@name

            break

            end -- insert new test

        end -- not disappeared

        if @opt_quiet=0 raiserror(@msg,10,1)

    end -- cursor cs

close cs
deallocate cs

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:

exec sp__usage @proc,''
Scope
    create case test and store it into tst table;
    ofcourse can test itself

Output
    ST|NNNNNNNN|OBJ|NAME
    ST is the status code
        ??  untested......: just inserted
        ok  passed........: the output is the same
        !#  error.........: inside sql error
        ko  failed........: the output is changed
        --  disappeared...: the object do not exists
    NNNNNNNN is the hexadecimal value of id

Parameters
    return      0 for ok; -1 for help; en error code
    @obj        the name of object to test or list or delete (accept %)
                if an ID (0x...between '''''''') show one line (see option too)
    @name       name/description of the test to run/del (accept %)
    @tst        the test code if different from sp
    @opt        options
                list    get a list of tests with results
                quiet   do not print output
                sel     return data as select instead of print
                purge   willing to @obj, remove tests without existing object
                del     if @obj is 0x... delete the test
                source  if @obj is 0x... show lines of source
                result  if @obj is 0x... show lines of result
                status  if @obj is 0x... show lines of status

    @dbg        debug level
                1       show dbg info

Examples
''

if not object_id(''tst'') is null
    begin
    exec sp__printf ''\n-- list of tests grouped by obj --''
    exec sp__select_astext
        ''select obj,count(*) n from tst group by obj order by obj''
    end

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
if @@trancount > 0 rollback -- for nested trans see style "procwnt"

-- exec sp__printf ''quit:%d, @failed:%s'',@quiet,@failed
-- print error_message()
exec @ret=sp__err @cod=@proc,@opt=''ex''

return @ret
end catch   -- proc sp__test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__test:

-- ============================================================= sp__test_server
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__test_server',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090122
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__test_server') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__test_server') with nowait
        goto skip_sp__test_server
        end
    if @ver>090122
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__test_server') with nowait
        goto skip_sp__test_server
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__test_server') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__test_server'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__test_server]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090122\S.Zaglio: again quoting management and expanded @cmd to 4000 chars
    v:081124\S.Zaglio: managed names with [] that osql don''t accept
    v:081114\S.Zaglio: now is indipendent from other my sp to allow simple trasport&run to remote server
    v:080916\S.Zaglio: a little modification moved @dbg as paramerter
    v:080806\S.Zaglio: added @db
    v:080429\S.Zaglio: added @uid & @pwd
    v:080429\S.Zaglio
    t: begin declare @r int exec @r=sp__test_server ''10.0.0.228'',''dontexist'' print @r end
    t: begin declare @r int exec @r=sp__test_server ''.'',''master'' print @r end ->1
    t: begin declare @r int exec @r=sp__test_server ''.'',''moster'' print @r end ->0
    t: begin declare @r int exec @r=sp__test_server ''.'','''' print @r end ->0
*/
CREATE procedure [dbo].[sp__test_server]
    @svr_name sysname,
    @db sysname,
    @ping_method bit=0,  -- 0=osql 1=ping
    @uid sysname=''sa'',
    @pwd sysname='''',
    @error nvarchar(4000)=null out,
    @dbg bit=0
as
BEGIN
set nocount on
declare @exists int
declare @crlf nchar(2)
SET @crlf=CHAR(13)+CHAR(10)
declare @cmd nvarchar(4000)
set @exists=0

set @svr_name=dbo.fn__sql_unquotename(@svr_name)
set @db=dbo.fn__sql_unquotename(@db)

if @ping_method=1 begin
    if @dbg=1 print ''ping method''
    CREATE TABLE #test_svr_temp ( pingResult SYSNAME NULL );
    set @cmd=''ping ''+@svr_name
    if @dbg=1 print @cmd
    INSERT #test_svr_temp
        EXEC master..xp_cmdshell @cmd
    IF EXISTS (
        SELECT 1
            FROM #test_svr_temp
            WHERE pingResult LIKE ''%TTL%''
        ) set @exists=1
    IF @dbg=1 SELECT * FROM #test_svr_temp
    DROP TABLE #test_svr_temp;
    end -- ping method
else
    begin -- osql method
    if @dbg=1 print ''osql method''
    IF EXISTS(SELECT 1 FROM dbo.sysobjects WHERE [ID] = OBJECT_ID(''#temp1'') AND type = (''U'')) drop table #temp1
    CREATE TABLE #temp1 (dbname SYSNAME NULL );
    set @cmd=''osql -S%svr_name% -dMaster -U%uid% -P%pwd% -Q"SELECT [Name] FROM sysdatabases where [name]=''''%db%''''"'';
    set @cmd=replace(@cmd,''%svr_name%'',@svr_name)
    set @cmd=replace(@cmd,''%uid%'',@uid)
    set @cmd=replace(@cmd,''%pwd%'',@pwd)
    set @cmd=replace(@cmd,''%db%'',@db)
    if @dbg=1 print @cmd
    INSERT #temp1
        EXEC master..xp_cmdshell   @cmd
    IF EXISTS (
        SELECT 1
            FROM #temp1
            WHERE LTRIM(RTRIM(dbname)) = @db
        ) set @exists=1
    IF @dbg=1 SELECT * FROM #temp1
    if @exists=0 begin
        -- exec sp__readtable ''#temp1'', @error output
        declare @txt sysname
        declare @sql nvarchar(4000)
        declare cs_tmp_svr cursor local for select dbname from #temp1
        open cs_tmp_svr
        while (1=1)
            begin
            fetch next from cs_tmp_svr into @txt
            if @@error != 0 or @@fetch_status != 0 break
            if coalesce(@error,'''')<>'''' set @error=@error+@crlf
            set @error=@error+@txt
            end  -- while
        close cs_tmp_svr
        deallocate cs_tmp_svr
        DROP TABLE #temp1
        end -- readtable
    end -- osql method
return @exists
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__test_server:

-- ============================================================== sp__token_name
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__token_name',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090811
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__token_name') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__token_name') with nowait
        goto skip_sp__token_name
        end
    if @ver>090811
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__token_name') with nowait
        goto skip_sp__token_name
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__token_name') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__token_name'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__token_name]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090811\s.zaglio: parse next tokens to extract a table path
    t:
        declare @path sysname,@s sysname,@i int

        select @s=''create proc svr.db.[schema].tbl (@param1 int)'',@i=13
        exec sp__parse_name @s,@path out,@i out
        exec sp__printf ''path=%s, next:%d'',@path,@i

        select @s=''create proc db.[schema].tbl (@param1 int)'',@i=13
        exec sp__parse_name @s,@path out,@i out
        exec sp__printf ''path=%s, next:%d'',@path,@i

        select @s=''create proc [schema].tbl (@param1 int)'',@i=13
        exec sp__parse_name @s,@path out,@i out
        exec sp__printf ''path=%s, next:%d'',@path,@i

        select @s=''create proc tbl (@param1 int)'',@i=13
        exec sp__parse_name @s,@path out,@i out
        exec sp__printf ''path=%s, next:%d'',@path,@i
*/
create proc [dbo].[sp__token_name]
    @line nvarchar(4000)=null out,  -- source code
    @path sysname       =null out,  -- output or found and normalized [server].[db].[schema].[name]
    @start int          =null out,  -- set/return next position after name
    @at int             =null out,
    @dbg  bit=0
as
begin
set nocount on
declare
    @token sysname,@tl sysname,@i int,@len int,
    @svr sysname,@db sysname,@schema sysname,@name sysname

select @path=null,@token=null,@tl=''.'',@i=@start,@len=len(@line)
exec sp__token @line,@path out,@i out,@at out
while @i<=@len and @tl=''.''
    begin
    select @token=null
    exec sp__token @line,@token out,@i out,@tl=@tl out
    if @tl=''.'' select @path=@path+@tl+@token,@start=@i
    end --
select
    @svr=   coalesce(dbo.fn__sql_quotename(parsename(@path,4))+''.'',''''),
    @db=    coalesce(dbo.fn__sql_quotename(parsename(@path,3))+''.'',''''),
    @schema=coalesce(dbo.fn__sql_quotename(parsename(@path,2))+''.'',''''),
    @name=  coalesce(dbo.fn__sql_quotename(parsename(@path,1)),''''),
    @path=@svr+@db+@schema+@name
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__token_name:

-- =============================================================== sp__top_waits
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__top_waits',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=080918
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__top_waits') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__top_waits') with nowait
        goto skip_sp__top_waits
        end
    if @ver>080918
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__top_waits') with nowait
        goto skip_sp__top_waits
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__top_waits') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__top_waits'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__top_waits]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:080918\S.Zaglio: originally from http://www.sqldbatips.com/showcode.asp?ID=3
*/
CREATE procedure sp__top_waits
(
   @interval nchar(8) = ''00:00:30'',   -- time between snapshots in seconds (1-59)
   @showall  int = 1                 -- show all waits longer than this value
)
as
/*
   Uses snapshot of waits to determine what''s waiting longest
   Some help with wait_type can be found at
   http://support.microsoft.com/default.aspx?scid=kb;en-us;Q244455
   Best reference found to date at
   http://sqldev.net/misc/WaitTypes.htm
*/
set nocount on

   create table #waits
   (
      runid               int identity(1,1)    NOT NULL,
      wait_type           sysname              NOT NULL,
      requests            float(53)            NOT NULL,
      wait_time           float(53)            NOT NULL,
      signal_wait_time    float(53)            NOT NULL,
      CONSTRAINT PK_waits PRIMARY KEY CLUSTERED
         (runid,wait_type)
   )

   insert #waits
   exec(''dbcc sqlperf(waitstats)'')

   waitfor delay @interval

   insert #waits
   exec(''dbcc sqlperf(waitstats)'')

   select a.wait_type,(b.requests-a.requests) as ''requests'',
          (b.wait_time-a.wait_time) as ''wait_time'',
          (b.signal_wait_time-a.signal_wait_time) as ''signal_wait_time''
   from   #waits a
   join   #waits b
   on     a.wait_type = b.wait_type and b.runid>a.runid
   where  (b.wait_time-a.wait_time) >= CAST(@showall as float(53))
   and    a.wait_type not in (''WAITFOR'', ''SLEEP'', ''RESOURCE_QUEUE'', ''Total'')
   order by wait_time desc
   option(KEEPFIXED PLAN)

   drop table #waits

return'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__top_waits:

-- ============================================================= sp__trace_event
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__trace_event',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110321
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__trace_event') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__trace_event') with nowait
        goto skip_sp__trace_event
        end
    if @ver>110321
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__trace_event') with nowait
        goto skip_sp__trace_event
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__trace_event') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__trace_event'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__trace_event]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,sync
    v:110321\s.zaglio: receive/send messages
*/
CREATE proc sp__trace_event
as
begin
set nocount on
declare @proc sysname,@ret int,@err int,@dbg int
select @proc=object_name(@@procid),@ret=0,@err=0,@dbg=isnull(@dbg,0)
print ''in sp__trace_event''

while (1=1)
    begin
    declare @body nvarchar(max),@type sysname
    waitfor (
        receive top(1)
            @type=message_type_name,
            @body=message_body
            from trace_queue
        ), timeout 500
    -- if there is no message exit
    if @@rowcount=0 break;
    if @type=''http://schemas.microsoft.com/SQL/Notifications/PostEventNotification''
        begin
        declare @data xml
        select @data=cast(@body as xml)
        declare @et sysname,@obj sysname,@sql nvarchar(max)
        select
            @et=EVENTDATA().value(''(/EVENT_INSTANCE/EventType)[1]'',''nvarchar(256)''),
            @obj=EVENTDATA().value(''(/EVENT_INSTANCE/ObjectName)[1]'',''nvarchar(256)''),
            @sql=EVENTDATA().value(''(/EVENT_INSTANCE/TSQLCommand)[1]'',''nvarchar(max)'')
        exec sp__trace_store @et,@obj,@sql
        end
    end -- while

end -- sp__trace_event'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__trace_event:

-- ============================================================= sp__trace_queue
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__trace_queue',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110321
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__trace_queue') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__trace_queue') with nowait
        goto skip_sp__trace_queue
        end
    if @ver>110321
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__trace_queue') with nowait
        goto skip_sp__trace_queue
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__trace_queue') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__trace_queue'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__trace_queue]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,trace
    r:110321\s.zaglio:sync scripts with gates and servers
*/
CREATE proc sp__trace_queue
as
begin
-- exec sp__log ''trace-sync''
print ''some tests but with errors on contract. See code''
goto ret

declare @h uniqueidentifier,@b varbinary(max),@t sysname

-- select db_name(database_id) db,object_name(queue_id) q,* from sys.dm_broker_queue_monitors

-- drop queue sync_queue
if object_id(''sync_queue'') is null
    create queue sync_queue with status=on, activation (procedure_name=sp__sync_queue, max_queue_readers = 1, EXECUTE AS ''tempsa'')

-- drop service sync_service
create service sync_service
on queue sync_queue

-- drop route sync_route
create route sync_route
with service_name=''sync_service'',address=''local''

-- drop message type sync_sql
create message type sync_sql

-- drop contract sync_contract
create contract sync_contract(sync_sql sent by any)

alter database utility
set enable_broker
with rollback immediate


-- declare @h uniqueidentifier
begin dialog conversation @h
    from service sync_service
    to service ''sync_service''
    on contract sync_contract
    with encryption=off

print @h

-- to prevent error 8429
-- select * from sys.conversation_endpoints
/*
alter queue trace_queue
with status = on , retention = off ,
activation ( status = off )

alter queue trace_queue
with status = on , retention = off ,
activation ( status = on )
*/
-- declare @h uniqueidentifier
select @h=''C9E9D502-C553-E011-B483-D48564540B5A''
;send on conversation @h
message type sync_sql(''test queue'')

-- select * from sys.transmission_queue
-- select db_name(database_id) db,object_name(queue_id) q,* from sys.dm_broker_queue_monitors
-- select * from sys.dm_broker_activated_tasks
select * from sync_queue

select @h=''C9E9D502-C553-E011-B483-D48564540B5A''
;receive top(1)
    @h=conversation_handle,
    @t=message_type_name,
    @b=message_body
from sync_queue

print @h
print @t
print convert(nvarchar(max),@b)
print convert(nvarchar(max),convert(xml,@b))

-- declare @h uniqueidentifier select @h=''CFA7D379-A653-E011-B483-D48564540B5A''
end conversation @h  -- also prevent error 8429

ret:
end -- sp__trace_sync'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__trace_queue:

-- ================================================================= sp__tree_fk
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__tree_fk',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=081207
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__tree_fk') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__tree_fk') with nowait
        goto skip_sp__tree_fk
        end
    if @ver>081207
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__tree_fk') with nowait
        goto skip_sp__tree_fk
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__tree_fk') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__tree_fk'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__tree_fk]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:081207\S.Zaglio: get foreing keys hierarchy
    c:originally from http://www.sqlservercentral.com/scripts/Maintenance+and+Management/30445/
*/
create proc sp__tree_fk
as
begin
/******************************************************************************
This script will run through the foreign keys on tables to produce a hierarchy
of the tables in a database.

The heirarchy produced will be :
0    Tables that have no FK relationships at all, as either as ''parents'' or
    ''children''
1    Tables which are at the top of the tree, and have no ''parents'', only
    ''children''
2    ...you can figure it out from here...



If you need to repopulate the database your table order would be 0,1,2...

To delete from tables you need to start at the highest number  ...3,2,1,0


*******************************************************************************/


SET NOCOUNT ON

DECLARE
    @intCounter    INT,
    @intRowCount    INT


CREATE TABLE #Hierarchy
    (Hierarchy    INT,
    Child        VARCHAR(100),
    Parent         VARCHAR(100))

-- Set the variables
SELECT @intCounter = 1
SELECT @intRowCount = 1


-- Populate the table
INSERT INTO #Hierarchy
SELECT DISTINCT 1 AS ''Hierarchy'', S1.name AS ''Child'', SO.Name AS ''Parent''
FROM dbo.sysforeignkeys FK
INNER JOIN dbo.sysobjects SO
ON FK.rkeyID = SO.id
INNER JOIN dbo.sysobjects S1
ON FK.fkeyID = S1.id



WHILE @intRowCount <> 0
BEGIN
    UPDATE #Hierarchy
    SET Hierarchy = Hierarchy + 1
    WHERE Hierarchy = @intCounter
    AND Parent IN  (SELECT DISTINCT Child
            FROM #Hierarchy
            WHERE Hierarchy = @intCounter)


    SET @intRowCount = @@Rowcount

    SELECT @intCounter = @intCounter + 1
END


-- Add the tables that have no Foriegn Key relationships...
INSERT INTO #Hierarchy
SELECT -1, [name], '' - ''
FROM dbo.sysobjects
WHERE [name] NOT IN (SELECT DISTINCT Parent FROM #Hierarchy)
AND [Name] NOT IN (SELECT DISTINCT Child FROM #Hierarchy)
AND xtype = ''U''


-- Add the tables that are Parents only
INSERT INTO #Hierarchy
SELECT DISTINCT 0, Parent, '' - ''
From #Hierarchy
WHERE Parent NOT IN (SELECT Child FROM #Hierarchy)
AND Hierarchy <> -1

-- Add 1 to adjust the hierarchies to start at 0
UPDATE #Hierarchy
SET Hierarchy = Hierarchy + 1


-- Display the results
SELECT DISTINCT Hierarchy, Child, Parent
FROM #Hierarchy
ORDER BY Hierarchy, Child, Parent

-- Clean up
DROP TABLE #Hierarchy
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__tree_fk:

-- ================================================================ sp__triggers
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__triggers',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=090805
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__triggers') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__triggers') with nowait
        goto skip_sp__triggers
        end
    if @ver>090805
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__triggers') with nowait
        goto skip_sp__triggers
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__triggers') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__triggers'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__triggers]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:090805\S.Zaglio: quotet db name
    v:080616\S.Zaglio: disable trigger for tables% of optional other db
*/
CREATE PROCEDURE [dbo].[sp__triggers]
    @action nchar(2)=''-?'',
    @tbl_like nvarchar(128)=''%'',
    @db_dst nvarchar(128)=null
AS
begin
declare @dbg bit
set @dbg=0
if @db_dst is null set @db_dst=db_name()
select @db_dst=dbo.fn__sql_quotename(@db_dst)
declare @sql nvarchar(512)
set @tbl_like='' and [name] like ''''''+@tbl_like+''''''''
if @action=''-?'' goto usage
if @action=''EA'' -- enable all
    begin
    if @dbg=1 print ''enable all triggers for ''+@tbl_like
    set @sql=''use ''+@db_dst+'' exec sp__foreachobj ''''trigger'''',@replacechar=''''$'''',@command1=''''alter table ? enable  trigger $'''',@whereand='''' and parent_obj=object_id(''''''''?'''''''')''''''
    if @dbg=1 exec sp__inject @sql out,''print '''''',''''''''
    exec sp__foreachobj ''TABLE'',@sql,@whereand=@tbl_like
    end
if @action=''DA'' -- disable all
    begin
    if @dbg=1 print ''disable all triggers for ''+@tbl_like
    set @sql=''use ''+@db_dst+'' exec sp__foreachobj ''''trigger'''',@replacechar=''''$'''',@command1=''''alter table ? disable trigger $'''',@whereand='''' and parent_obj=object_id(''''''''?'''''''')''''''
    if @dbg=1 exec sp__inject @sql out,''print '''''',''''''''
    exec sp__foreachobj ''TABLE'',@sql,@whereand=@tbl_like
    end
return
usage:
    print ''sp__triggers''
    print ''    @action nchar(2)=''''-?''''''
    print ''    @tbl_like nvarchar(128)=''''%''''''
    print ''    @db_dst nvarchar(128)=null''
    print ''usage:''
    print '' sp_t -? this help''
    print '' sp_t EA enable all triggers''
    print '' sp_t DA disable all triggers''
    return
test:
    exec sp__triggers ''DA'',@tbl_like=''a%''
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__triggers:

-- ==================================================================== sp__trim
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__trim',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__trim') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__trim') with nowait
        goto skip_sp__trim
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__trim') with nowait
        goto skip_sp__trim
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__trim') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__trim'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__trim]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091018\S.Zaglio: strim chars on left an right from strings
    s:fn__trim
    t:
        declare @st nvarchar(4000),@crlf nchar(2)
        select @crlf=char(13)+char(10),@st=''
            select X
            from Y

''
        exec sp__trim @st out,@crlf
        print ''/''+''*''+@st+''*''+''/''
        exec sp__trim @st out
        print ''/''+''*''+@st+''*''+''/''
        select @st=''hello world!''
        exec sp__trim @st out
        print ''/''+''*''+@st+''*''+''/''
    exec sp__trim @st out,@crlf
    print @st
*/
CREATE proc sp__trim
    @st nvarchar(4000) out,
    @chars nvarchar(2)='' ''
as
begin
declare @lchars int,@x1 int,@x2 int,@lst int,@c1 nchar(1),@c2 nchar(1)
select @c1=substring(@chars,1,1),@c2=substring(@chars,2,1)
if @c1='''' and @c2 is null select @st=ltrim(rtrim(@st))
else
    begin
    select @lst=len(@st),@lchars=1,@x1=1,@x2=@lst
        while @x2>0 and substring(@st,@x2,1) in (@c1,@c2)
    select @x2=@x2-@lchars
    while @x1<@lst and substring(@st,@x1,1) in (@c1,@c2)
        select @x1=@x1+@lchars
    -- print dbo.fn__hex(convert(varbinary(4000),@st))
    -- exec sp__printf ''x1=%d,x2=%d'',@x1,@x2
    select @st=substring(@st,@x1,@x2-@x1+1)
    end
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__trim:

-- ================================================================ sp__try_exec
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__try_exec',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110705
        begin
        if @aut!='l.mazzanti'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__try_exec') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__try_exec') with nowait
        goto skip_sp__try_exec
        end
    if @ver>110705
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__try_exec') with nowait
        goto skip_sp__try_exec
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__try_exec') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__try_exec'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__try_exec]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file                 --optional
    g:utility
    r:110705\l.mazzanti: aggiorna l''if_erp_sent, ritentando se necessario
    t: exec @ret = sp__try_exec ''UPDATE SINTESI_STO11_MOVEMENTS_SN SET IF_ERP_SENT = 10 WHERE IF_ERP_SENT = 101'', 3
*/
CREATE PROCEDURE sp__try_exec
(
    @sql nvarchar(4000),
    @retries int
)
AS
BEGIN
    SET NOCOUNT ON

    declare @cnt int, @errorset bit
    select @errorSet=0,@cnt=0

    upd_retry:

        BEGIN TRY
            IF @cnt<@retries
            BEGIN
                SET LOCK_TIMEOUT 10000

                exec (@sql)
                exec sp__printf ''Esecuzione ''''%s'''' | rowcount: %d'', @sql, @@rowcount

                SET LOCK_TIMEOUT -1
            END
            ELSE
            BEGIN
                SET @errorSet = 1
            END
        END TRY
        BEGIN CATCH
            SET @cnt = @cnt+1
            EXEC sp__printf ''Fallito tentativo n.%s'', @cnt
            WAITFOR DELAY ''00:00:05''
            GOTO upd_retry
        END CATCH

        IF @errorSet = 1 return -1
        return 0

        SET NOCOUNT OFF
END'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__try_exec:

-- ================================================================== sp__update
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__update',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=081219
        begin
        if @aut!='S.Zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__update') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__update') with nowait
        goto skip_sp__update
        end
    if @ver>081219
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__update') with nowait
        goto skip_sp__update
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__update') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__update'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__update]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:081219\S.Zaglio: added @rows
    v:081208\S.Zaglio: done simple random update
    c:(info at http://msdn.microsoft.com/en-us/library/aa175776(SQL.80).aspx)
    t:sp__update ''ot04_stock'',@excludes=''sync_id,sync_dt'',@random=1,@dbg=1
    t:sp__update ''ot04_stock'',@flds=''ID_TRK_PRODUCER'',@where=''sync_id'',@random=1,@dbg=1
*/
CREATE          proc [dbo].[sp__update]
    @table sysname,
    @flds nvarchar(4000)=null,@excludes nvarchar(4000)=null,
    @values nvarchar(4000)=null,
    @where nvarchar(4000)=null,
    @rows bigint=null out,
    @random bit=0,
    @dbg bit=0
as
begin
set nocount on
declare @crlf nvarchar(2) set @crlf=char(13)+char(10)
declare @sets nvarchar(4000)
declare @sql  nvarchar(4000)
declare @test nvarchar(4000)
declare @d datetime set @d=getdate()
if @flds is null set @flds=dbo.fn__flds_of(@table,'','',@excludes)
if @random=1 begin
    if @where is null set @where=dbo.fn__flds_of(@table,'','',null)
    set @sql=''SELECT top 1 ''+@where+'' FROM ''+@table+'' ORDER BY newid() desc'' -- get a random line
    set @values=''''
    exec sp__select @sql,@body=@values out,@null=''(/null/)'',@dtstyle=126
    set @values=dbo.fn__inject(@values)
    set @where=replace(@where,'','','' and '')
    set @where=dbo.fn__str_exp(dbo.fn__str_exp(''%%=''''@@'''''',@where,'' and ''),@values,''|'')
    set @where=replace(@where,''=''''(/null/)'''''','' is null '')
    set @sql=''SELECT top 1 ''+@flds+'' FROM ''+@table+'' WHERE ''+@where
    set @values=''''
    set @test=''SELECT top 1 ''+@excludes+'' FROM ''+@table+'' WHERE ''+@where

    exec sp__select @sql,@body=@values out,@null=''(/null/)'',@dtstyle=126
    if coalesce(@values,'''')='''' begin print ''error:''+@sql+@crlf+''      ''+dbo.fn__inject(@sql) goto ret end
    set @values=dbo.fn__inject(@values)
    set @sets =dbo.fn__str_exp(dbo.fn__str_exp(''%%=''''@@'''''',@flds,'',''),@values,''|'')
    set @sets=replace(@sets,''=''''(/null/)'''''',''=null '')
end
if not @where is null set @where ='' where ''+@where else set @where=''''
set @sql=''update %tbl%\n set %sets%\n %where%''
exec sp__str_replace @sql out,''\n|%tbl%|%sets%|%where%'',@crlf,@table,@sets,@where
if @dbg=1 exec sp__printf @sql
exec(@sql)
set @rows=@@rowcount
if @rows!=1 print ''no updated row''
--exec sp__printf dbo.fn__sql_format(@sql,80)
ret:
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__update:

-- ================================================================= sp__upgrade
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__upgrade',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130922
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__upgrade') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__upgrade') with nowait
        goto skip_sp__upgrade
        end
    if @ver>130922
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__upgrade') with nowait
        goto skip_sp__upgrade
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__upgrade') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__upgrade'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__upgrade]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:compare, deploy, upload, download,
    d:130902\s.zaglio:sp__checkout
    d:130902\s.zaglio:sp__script_deploy
    r:130922,130906,130905\s.zaglio:adapting to sp__file_get
    v:130830\s.zaglio:done remake
    r:130829\s.zaglio:rename and remake from sp__checkout
    v:130204\s.zaglio:working near comparation
    r:130201\s.zaglio:help to identify diff of versions
    t:sp__upgrade @opt=''list''
*/
CREATE proc sp__upgrade
    @uri nvarchar(max) = null,
    @opt sysname = null,
    @dbg int = 0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    @i int,@j int,@n int,
    @run bit,@noquote bit,
    @crlf varchar(2),@cr char(1),@lf char(1),
    @data varbinary(max),@cdata nvarchar(max),                 -- character data
    @grp sysname,@temp nvarchar(1024),
    @todo varchar(16),@obj sysname,
    @q char,@qq char(2)

-- =========================================================== initialization ==
exec sp__get_temp_dir @temp out
select
    @crlf=crlf,@cr=cr,@lf=lf,
    @q=''"'',@qq=''""'',
    @uri=replace(ltrim(rtrim(isnull(@uri,''''))),''%temp%'',@temp),
    @run=charindex(''|run|'',@opt)
from fn__sym()

-- ======================================================== second params chk ==
if @uri='''' goto help
if @uri like ''%,%,%,%,%'' select @cdata=@uri,@uri=null
if not @uri like ''http%.asmx?op=%''
if not @uri like ''%[\/]index.txt''
    raiserror(''@uri must end with "index.txt" file name'',16,1)

-- =============================================================== #tbls init ==

select top  0 * into #objs_list
from fn__script_group_select(default,default,default,''top0'',default)

-- temp table for index file
select top 0 obj,tag,ver,aut,des,lcl
into #group_file_list
from #objs_list

-- drop table #src
create table #src(lno int identity primary key,line nvarchar(4000))

-- ===================================================================== body ==

/*
    1. download list (or parse @uri as list)
    2. get the group if exists
    3. get current list
    4. compare ...
*/

-- ============================================================ download list ==

-- sp__upgrade ''sp__upgrade,v,130831,s.zaglio,"rename and remake","done remake"''
-- sp__upgrade ''sp__upgrade,v,130831,s.zaglio,"rename and remake","last chglog"''

if @cdata is null
    begin
    exec sp__file_get @uri out,@data out
    select @cdata=cast(@data as nvarchar(max))
    end

-- fill table and get group name .. if given
insert #group_file_list(obj,tag,ver,aut,des,lcl)
select c00,c01,c02,c03,c04,c05
from fn__ntext_to_lines(@cdata,0)
cross apply fn__str_words(line,'','',''csv'')

select @grp=obj from #group_file_list where tag=''''

-- ===================================================== fill with local list ==
insert into #objs_list
select *
from fn__script_group_select(@grp,default,default,''lcl'',default)

-- ======================================================= compare into #todo ==

-- t:sp__upgrade ''%temp%\utility\index.txt''

select
    todo=
    case when ol.obj is null then ''add''
         when isnull(ol.lcl,'''')!=isnull(gl.lcl,'''') then ''conflict''
         when isnull(ol.lcl,'''')=isnull(gl.lcl,'''')
         then case
              when ol.ver>gl.ver then ''newest''
              when ol.ver<gl.ver then ''update''
              else ''nothing''
              end
    end,
    obj=
    isnull(ol.obj,gl.obj),
    l_tag=ol.tag,
    r_tag=gl.tag,
    l_ver=ol.ver,
    r_ver=gl.ver,
    l_aut=ol.aut,
    r_aut=gl.aut,
    l_des=ol.des,
    r_des=gl.des,
    l_lcl=ol.lcl
into #todo
from #group_file_list gl
full join #objs_list ol
on gl.obj=ol.obj
where gl.tag in (''r'',''v'') and ol.tag in (''r'',''v'')

insert into #todo
select ''drop'',obj,'''',gl.tag,'''',gl.ver,'''',gl.aut,'''',gl.des,gl.lcl
from #group_file_list gl
where gl.tag=''d''

-- ================================================== show results or upgrade ==
if @run=0
    begin
    insert #src
    select ''todo,obj,l_tag,r_tag,l_ver,r_ver,l_aut,r_aut,l_des,r_des,l_lcl''
    insert #src
    select
        @q+todo+@q+'',''+
        @q+obj+@q+'',''+
        @q+l_tag+@q+'',''+
        @q+r_tag+@q+'',''+
        @q+l_ver+@q+'',''+
        @q+r_ver+@q+'',''+
        @q+l_aut+@q+'',''+
        @q+r_aut+@q+'',''+
        @q+replace(l_des,@q,@qq)+@q+'',''+
        @q+replace(r_des,@q,@qq)+@q+'',''+
        @q+replace(l_lcl,@q,@qq)+@q
    from #todo
    order by todo desc,obj
    select * from #src
    end
else
    begin
    declare cs cursor local for
        select todo,obj
        from #todo
        where 1=1
    open cs
    while 1=1
        begin
        fetch next from cs into @todo,@obj
        if @@fetch_status!=0 break

        if @todo=''drop''
            begin
            exec sp__deprecate @obj
            exec sp__printf ''-- %s dropped'',@obj
            end

        if @todo in (''newest'',''conflict'')
            exec sp__printf ''-- %s send not implemented'',@obj

        if @todo=''update''
            begin
            print ''todo download and run''
            end
        end -- cursor cs
    close cs
    deallocate cs
    end -- run

goto ret

-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    download list from a url or a file or a constant and compare versions
    with local objects; if RUN options specified, execute the upgrade.

Notes
    - conflicts:
        case    svr obj ver aut pver paut   (p stay for previouse)
        1       s1  o1  t1  A   t0   A
        2       s2  o1  t1  A   t0   A
        3       s3  o1  t1  B   t0   A
        4       s1  o2  t1  A   t0   A
        5       s2  o2  t2  B   t0   A

        case 1 and 2: must not happen because is undecidable
        case 2 and 3: same version but different user -> conflict
        case 4 and 5: if the happen together, there is no way to solve
                      if 4 happen before 5, 5 see a difference

      the new object with conflict is stored into LOG_DDL and difference
      can be explored using SP__SCRIPT_DIFF.

Parameters
    @uri    is the source of list (see sp__script_group_tofile); can be:
            - url of "index.txt" (index.txt must be included)
            - path of "index.txt" (index.txt must be included)
            - the source of the list (for manual comparision)
    @opt    options
            run     execute the upgrade instead of list the differencies
            newest  add to the output the source of newest objects

Examples
    -- load list from file and compare with local
    exec sp__upgrade "%temp%\utility\index.txt"
    -- compare current "sp__upgrade" version with the specified
    exec sp__upgrade ''''sp__upgrade,v,130829,s.zaglio,"rename and remake"''''
    --
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__upgrade'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__upgrade:

-- =================================================================== sp__usage
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__usage',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120824
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__usage') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__usage') with nowait
        goto skip_sp__usage
        end
    if @ver>120824
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__usage') with nowait
        goto skip_sp__usage
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__usage') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__usage'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__usage]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120824\s.zaglio: added usage of #sp
    v:120112\s.zaglio: now print @obj version near usage of ...
    v:110706\s.zaglio: added odd/event test for presence of "
    v:110628\s.zaglio: a bug near info on fn
    v:110513\s.zaglio: a small bug
    v:110510\s.zaglio: replace " with '' on line starting with sp_
    v:110507\s.zaglio: replace " with '' if inside sql code
    v:110422\s.zaglio: expanded @extra to blob
    v:110406\s.zaglio: added @p1...,@opt, removed old @test params
    v:110312\s.zaglio: added autoreverse if remote call
    v:100919.1000\s.zaglio: more compatible mssql2k
    v:100919\s.zaglio: out of extra without replacement of macro
    v:100723\s.zaglio: @force=0 in print of @extra (for sp__style)
    v:100707\s.zaglio: show info about reversed
    v:100501\s.zaglio: added @reverse
    v:100404\s.zaglio: added #vars replacements
    v:100228\s.zaglio: added db name support
    v:090222\s.zaglio: addded out&null info
    v:090805\S.Zaglio: removed @force=0 on printf
    v:090729\S.Zaglio: revision
    v:090623\S.Zaglio: revision
    v:090616\S.Zaglio: added @extra
    v:090610\S.Zaglio: added @print
    v:090608\S.Zaglio: added -- to use as template for sp call
    v:090520\S.Zaglio: added more info to params
    v:081117\S.Zaglio: made indipendent from sp__dir
    v:081021\S.Zaglio: print a fast & simple expanation of parameters of sp (future expansion)
    t:sp__usage ''fn__comments''
*/
CREATE PROCEDURE [dbo].[sp__usage]
    @obj   sysname=null,
    @extra ntext=null,  -- extra help until will be integrated in a help system
    @p1    sql_variant=null,
    @p2    sql_variant=null,
    @p3    sql_variant=null,
    @p4    sql_variant=null,
    @opt   sysname=null
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0,
       @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @obj is null goto help

create table #src (lno int identity,line nvarchar(4000))
-- insert #src(line) select ''test''
create table #blob(id int identity,blob ntext)
create table #cols(id int identity,name sysname,colorder int)

declare
    @n int,@db sysname,@reverse bit,@id bigint,@sql nvarchar(4000),
    @sel bit,@xt nvarchar(2),@ver nvarchar(32)

select
    @reverse=dbo.fn__isremote(@@spid),
    @sel=charindex(''|sel|'',@opt)

if left(@obj,1)=''#''
    begin
    select @id=object_id(''tempdb..''+@obj)
    select @xt=xtype from tempdb..sysobjects with (nolock) where id=@id
    insert #cols select name,colorder
    from tempdb..syscolumns c with (nolock)
    where left(c.name,1)=''@''
    end
else
    begin
    select @id=object_id(@obj)
    select @xt=xtype from sysobjects with (nolock) where id=@id
    insert #cols
    select name,colorder from syscolumns c with (nolock)
    where left(c.name,1)=''@''
    end


if @id is null goto err_obj

if (@xt) in (''FN'',''TF'',''IF'')
    begin
    -- select * from syscolumns where id=object_id(''fn__comment'')
    -- select * from syscolumns where id=object_id(''fn__comments'')
    select @sql=null
    select @sql=isnull(@sql+'','','''')+''/*''+c.[name]+''*/default''
    from #cols
    order by c.colorder
    if @xt=''IF''
        select @sql=''select dbo.''+@obj+''(''+@sql+'')''
    select @sql=''select * from [''+@obj+''](''+@sql+'')''
    if @sel=1 select @sql else print @sql
    if @extra is null goto ret
    end --

select @ver='' (''+tag+'':''+convert(sysname,val1)+'') ''
from dbo.fn__script_info(@obj,''rv'',0)
select @ver=isnull(@ver,'''')

insert #src(line)
select ''usage of '' + @obj+@ver+
                     case @reverse
                     when 1
                     then ''(list is reversed for remote servers)''
                     else ''''
                     end

-- print parsename(''db..obj'',3)
-- print isnull(parsename(''obj'',3),''??'')
select @db=parsename(@obj,3)
if @db is null select @db=db_name()
-- print parsename(''obj'',1)
select @obj=parsename(@obj,1)

exec(''
use [''+@db+'']
declare @n int,@obj sysname select @obj=''''''+@obj+''''''
select @n=max(len(name)) from syscolumns with (nolock) where id=object_id(@obj)
insert into #src(line)
select
    ''''    ''''+left(c.[name]+space(@n),@n) +
    '''' ''''+case
        when t.name in (''''nvarchar'''',''''nchar'''') then t.name+''''(''''+convert(sysname,c.length/2)+'''')''''
        when t.name in (''''char'''',''''varchar'''') then t.name+''''(''''+convert(sysname,c.length)+'''')''''
        else t.name end+
    -- case when c.isnullable=1 then ''''=null'''' else '''''''' end+   because all def params are nullable
    '''' ''''+case when c.isoutparam=1 then ''''out'''' else '''''''' end+
    '''','''' as line
from syscolumns c with (nolock)
inner join systypes t with (nolock)  on c.xusertype=t.xusertype
where id=object_id(@obj) and left(c.name,1)=''''@'''' order by colid
'')

insert #blob(blob) select @extra
exec sp__write_ntext_to_lines @crlf=0
drop table #blob

if not object_id(''tempdb..#vars'') is null
    exec sp__str_replace ''#src'',''#vars''

update #src set line=replace(line,''%proc%'',@obj)
if not @p1 is null update #src set line=replace(line,''%p1%'',convert(nvarchar(4000),@p1,126))
if not @p2 is null update #src set line=replace(line,''%p2%'',convert(nvarchar(4000),@p2,126))
if not @p3 is null update #src set line=replace(line,''%p3%'',convert(nvarchar(4000),@p3,126))
if not @p4 is null update #src set line=replace(line,''%p4%'',convert(nvarchar(4000),@p4,126))

-- replace " with '' if even occurrences and in particular cases
update #src set line=replace(line,''"'','''''''')
where 1=1
and (charindex(''"'',line)>0 and dbo.fn__str_count(line,''"'')%2=1)
and (
    charindex('' '',ltrim(line))>0
    and (   left(ltrim(line),charindex('' '',ltrim(line))-1) in
                (''select'',''insert'',''exec'',''if'',''else'',''update'',''declare'',''set'')
        or left(ltrim(line),3)=''sp_''
        )
    or left(ltrim(line),1)=''@''
    )
if @sel=1
    select line from #src order by lno
else -- print
    exec sp__print_table ''#src'',@reverse=@reverse

drop table #src
goto ret

-- =================================================================== errors ==
err_obj:    exec @ret=sp__err ''object not found'' goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope of %proc% Version 1.04.110406
    print info and help about an object

Notes
    * if the object is a function, add a line of code with its call
    * if called remotelly, print help in reverse order (MS remote print problem)
    * if declare #vars, inside tokens are replaced (see sp__str_replace)
    * *********************************************
    * ** this help is a template for all others  **
    * *********************************************
    * future strategy is to use this sp to populate wiki table
    * in the lines that start with "exec","select",etc or "sp_"
      is considered sql code and " will be replaced with ''''

Parameters
    @obj    name of object
    @extra  info/help text
            if @extra contain macros, this will be replaced
            macro   mean
            %proc%  replaced with @obj
            %p1%    replaced with @p1
            %p...%  replaced with @p...
    @opt    options
            sel     return help as select instead of print

Examples

    exec sp__usage ''''sp__usage'''',''''
    Scope of %obj%
        this is an examples with replace %p1%
        '''',
        @p1=''''p1_test''''
''
select @ret=-1

ret:
return @ret
end -- sp__usage'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__usage:

-- ============================================================ sp__util_1strace
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_1strace',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=101130
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_1strace') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_1strace') with nowait
        goto skip_sp__util_1strace
        end
    if @ver>101130
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_1strace') with nowait
        goto skip_sp__util_1strace
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_1strace') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_1strace'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_1strace]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:101130\s.zaglio: show current trace info
    t:select * from ::fn_trace_getinfo(default)
*/
create proc sp__util_1strace
as
begin

-- SELECT 0, TextData, DatabaseID, ApplicationName, Duration, EndTime, Reads, Writes, CPU
select StartTime,EndTime,TextData,Reads, Writes, CPU,*
from fn_trace_gettable((select convert(nvarchar(512),[value]) from ::fn_trace_getinfo(default) where traceid=1 and [property]=2), default)
where 1=1
and StartTime>getdate()-0.1
and TextData IS NOT NULL

end -- sp__util_1strace'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_1strace:

-- ============================================================== sp__util_3load
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_3load',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100508
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_3load') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_3load') with nowait
        goto skip_sp__util_3load
        end
    if @ver>100508
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_3load') with nowait
        goto skip_sp__util_3load
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_3load') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_3load'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_3load]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100508\s.zaglio: load/reload data from a remote server, triangulating from a 2nd rmtsvr
    c: this <- tsvr <- rmtsvr2
    t:sp__util_3load ''tbl'',''tsvr'',''rsvr'',@truncate=1,@dbg=1
*/
CREATE proc sp__util_3load
    @tbl    sysname=null,
    @tsvr   sysname=null,
    @rsvr   sysname=null,
    @rdb    sysname=null,
    @truncate bit=1,
    @err    int=null out,
    @rows    bigint=null out,
    @dbg    bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=''sp__util_3load'',@ret=1

if @tbl is null goto help

declare
    @tmp sysname,@sql nvarchar(4000),@db sysname,
    @trunc sysname,@sch sysname

select
    @rsvr=dbo.fn__sql_quotename(quotename(@rsvr)),
    @db=quotename(db),@sch=quotename(sch),@tbl=quotename(obj)
from dbo.fn__parsename(@tbl,default,default)

select @tbl=@sch+''.''+@tbl
if @rdb is null select @rdb=@db

select @tmp=''tempdb..tmp_''+dbo.fn__str_guid(newid())
if @truncate=1 select @trunc=''truncate table ''+@tbl

select @sql=''
use %db%
exec %tsvr%.tempdb.dbo.sp_executesql
    N"select * into %tmp% from %rsvr%.%rdb%.%tbl%"

-- dont''''t look at me; it is required to elude a "Deferred prepare could not be completed"
exec("select * into %tmp% from openquery(%tsvr%,""select * from %tmp%"")")

exec %tsvr%.tempdb.dbo.sp_executesql N"drop table %tmp%"

begin tran
%truncate%
if objectproperty(object_id("%tbl%"),"TableHasIdentity")=1
    set identity_insert %tbl% on
insert into %tbl% select * from %tmp%
select @err=@@error,@rows=@@rowcount
if objectproperty(object_id("%tbl%"),"TableHasIdentity")=1
    set identity_insert %tbl% off
if @err=0 commit else rollback
drop table %tmp%
''

exec sp__str_replace @sql out,
    ''"|%db%|%tsvr%|%tmp%|%rsvr%|%rdb%|%tbl%|%truncate%'',
    '''''''',@db,@tsvr,@tmp,@rsvr,@rdb,@tbl,@trunc

if @dbg=1 print @sql
else
    exec sp_executesql @sql,N''@err int out,@rows bigint out'',
                            @err=@err out,@rows=@rows out

goto ret

help:
exec sp__usage @proc,''

Scope
    load/reload data from a remote server, triangulating from a 2nd rmtsvr
''

ret:
return @ret
end -- sp__util_load'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_3load:

-- ============================================================= sp__util_advopt
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_advopt',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100405
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_advopt') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_advopt') with nowait
        goto skip_sp__util_advopt
        end
    if @ver>100405
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_advopt') with nowait
        goto skip_sp__util_advopt
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_advopt') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_advopt'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_advopt]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    g:utility
    v:100405\s.zaglio: show lòcal ad options
    v:100228\s.zaglio: activate advanced option on remote linked server
    t:sp__util_advopt ''.''
*/
CREATE proc [dbo].[sp__util_advopt] @svr sysname=null
as
begin
set nocount on
if @svr is null goto help

declare @sql nvarchar(4000)

select @sql=''
EXECUTE %svr%..sp_configure "show advanced options", 1 RECONFIGURE WITH OVERRIDE
reconfigure
EXECUTE %svr%..sp_configure "Ad Hoc Distributed Queries",1 RECONFIGURE WITH OVERRIDE
EXECUTE %svr%..sp_configure "xp_cmdshell", "1" RECONFIGURE WITH OVERRIDE
EXECUTE %svr%..sp_configure "Ole Automation Procedures", "1" RECONFIGURE WITH OVERRIDE
EXECUTE %svr%..sp_configure "SMO and DMO XPs", "1" RECONFIGURE WITH OVERRIDE
EXECUTE %svr%..sp_configure "clr enabled", "1" RECONFIGURE WITH OVERRIDE
-- MSSQL2008
-- EXECUTE sp_configure "Ad Hoc Distributed Queries",1 RECONFIGURE WITH OVERRIDE
EXECUTE %svr%..sp_configure
EXECUTE %svr%..sp_configure "show advanced options", 0 RECONFIGURE WITH OVERRIDE
reconfigure
''
select @sql=replace(@sql,''"'','''''''')
if @svr=''.'' select @sql=replace(@sql,''%svr%..'','''')
else select @sql=replace(@sql,''%svr%'',''[''+@svr+'']'')
exec sp__printf @sql
exec(@sql)
goto ret

help:
exec sp__printf ''Enable advanced options (openroset,xp_cmdshell, etc) on remote server''
exec sp__printf ''Use "." on local server''
EXECUTE sp_configure "show advanced options", 1 RECONFIGURE WITH OVERRIDE
reconfigure
EXECUTE sp_configure
EXECUTE sp_configure "show advanced options", 0 RECONFIGURE WITH OVERRIDE
reconfigure

ret:
end -- sp__util_advopt'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_advopt:

-- ======================================================== sp__util_auto_growth
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_auto_growth',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130227
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_auto_growth') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_auto_growth') with nowait
        goto skip_sp__util_auto_growth
        end
    if @ver>130227
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_auto_growth') with nowait
        goto skip_sp__util_auto_growth
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_auto_growth') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_auto_growth'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_auto_growth]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:monitor,database,performance,auto,grouth
    v:130227\s.zaglio: identifying how often an auto-growth event has occurred
    c:
        from
        https://www.simple-talk.com/sql/database-administration/sql-server-database-growth-and-autogrowth-settings/
    t:sp__util_auto_growth run
*/
create proc sp__util_auto_growth
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==
declare
    -- generic common
    @run bit,
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @sel bit,@print bit,                -- select and print option for utils
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @sel=charindex(''|sel|'',@opt),@print=charindex(''|print|'',@opt),
    @run=charindex(''|run|'',@opt)|dbo.fn__isjob(@@spid)
        |cast(@@nestlevel-1 as bit),        -- when called by parent/master SP
    @end_declare=1

-- if @print=0 and @sel=0 and dbo.fn__isConsole()=1 select @print=1

-- ======================================================== second params chk ==
if @run=0 goto help


-- =============================================================== #tbls init ==

-- ===================================================================== body ==

declare @filename nvarchar(1000);
declare @bc int;
declare @ec int;
declare @bfn varchar(1000);
declare @efn varchar(10);

-- get the name of the current default trace
select @filename = cast(value as nvarchar(1000))
-- select *
from ::fn_trace_getinfo(default)
where traceid = 1 and property = 2;

-- rip apart file name into pieces
set @filename = reverse(@filename);
set @bc = charindex(''.'',@filename);
set @ec = charindex(''_'',@filename)+1;
set @efn = reverse(substring(@filename,1,@bc));
set @bfn = reverse(substring(@filename,@ec,len(@filename)));

-- set filename without rollover number
set @filename = @bfn + @efn

-- process all trace files
select
  ftg.starttime
,te.name as eventname
,db_name(ftg.databaseid) as databasename
,ftg.filename
,(ftg.integerdata*8)/1024.0 as growthmb
,(ftg.duration/1000)as durms
from ::fn_trace_gettable(@filename, default) as ftg
inner join sys.trace_events as te on ftg.eventclass = te.trace_event_id
where (ftg.eventclass = 92  -- date file auto-grow
    or ftg.eventclass = 93) -- log file auto-grow
order by ftg.starttime

-- sp__find ''fn_trace_getinfo''

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    identifying how often an auto-growth event has occurred

Parameters
    @opt    options
            run     execute

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__util_auto_growth'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_auto_growth:

-- ======================================================== sp__util_cache_empty
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_cache_empty',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091209
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_cache_empty') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_cache_empty') with nowait
        goto skip_sp__util_cache_empty
        end
    if @ver>091209
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_cache_empty') with nowait
        goto skip_sp__util_cache_empty
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_cache_empty') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_cache_empty'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_cache_empty]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091209\s.zaglio
*/
create proc sp__util_cache_empty
as
begin
checkpoint;
dbcc dropcleanbuffers;
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_cache_empty:

-- ======================================================== sp__util_connections
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_connections',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121025
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_connections') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_connections') with nowait
        goto skip_sp__util_connections
        end
    if @ver>121025
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_connections') with nowait
        goto skip_sp__util_connections
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_connections') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_connections'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_connections]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:tcp,ip,host,status,client
    v:121025\s.zaglio: list active connections and some useful info
    t:sp__util_connections #
    t:sp__util_connections ''%hostname%''
*/
CREATE proc sp__util_connections
    @what sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @opt1 bit,@opt2 bit,
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @opt1=charindex(''|opt|'',@opt),
    @end_declare=1

-- ======================================================== second params chk ==
if @what is null goto help

-- ===================================================================== body ==

if @what=''#'' select @what=''%'' else select @what=''%''+@what+''%''
select @what=replace(@what,''%hostname%'',host_name())

select
    c.session_id,
    p.kpid,
    p.lastwaittype,p.waitresource,
    p.status,p.hostname,p.program_name,p.loginame,
    db_name(p.dbid) db,
    c.most_recent_session_id,
    c.connect_time,
    c.num_reads,    c.num_writes,
    c.last_read,    c.last_write,
    c.client_net_address,
    c.client_tcp_port,
    c.local_net_address,
    c.local_tcp_port,
    object_name(s.objectid,s.dbid) obj,
    substring(s.text,p.stmt_start/2,
    (case when p.stmt_end = -1
    then len(convert(nvarchar(max), s.text)) * 2
    else p.stmt_end end - p.stmt_start+3)/2)
    as sql
    --most_recent_sql_handle
    /*
    SELECT substring(text,x.statement_start_offset/2,
    (case when x.statement_end_offset = -1
    then len(convert(nvarchar(max), text)) * 2
    else x.statement_end_offset end - x.statement_start_offset+3)/2)
    -- select * from
    FROM sys.dm_exec_sql_text(x.sql_handle)
    FOR XML PATH(''''), TYPE
    ) AS Sql_text
    */

-- select *
from sys.dm_exec_connections c (nolock)
join sys.sysprocesses p (nolock) on c.session_id=p.spid
-- join sys.dm_exec_requests r on c.session_id=r.spid
cross apply sys.dm_exec_sql_text(p.sql_handle) as s
where 1=1
and p.spid!=@@spid
and @what=''%''
or (
    p.status like @what
    or p.hostname like @what
    or p.program_name like @what
    or p.loginame like @what
    or db_name(p.dbid) like @what
    )

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
/*
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_me1:    select @e_msg=''write here msg''                              goto err
err_me2:    select @e_msg=''write this %s'',@e_p1=@var                    goto err
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    list active connections and some useful info

Parameters
    @what   a generic filter for each text field
            use # or ''''%'''' for everithing
    @opt    not used
    @dbg    not used

Examples
    t:sp__util_connections #
    t:sp__util_connections ''''%hostname%''''
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__util_connections'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_connections:

-- ============================================================= sp__util_drives
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_drives',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130802
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_drives') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_drives') with nowait
        goto skip_sp__util_drives
        end
    if @ver>130802
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_drives') with nowait
        goto skip_sp__util_drives
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_drives') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_drives'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_drives]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:%licence%
    g:utility
    v:130802\s.zaglio: about drop of #drive_info
    v:130227\s.zaglio: added dbs and logs flds
    v:130216\s.zaglio: remake
    v:100212\s.zaglio: drives info
    c:from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=92571
    t:sp__util_drives run
*/
CREATE proc sp__util_drives
    @opt sysname = null,
    @dbg bit=null
as
begin
set nocount on

declare @proc sysname,@ret int

select
    @proc=object_name(@@procid),
    @ret=0,
    @dbg=isnull(@dbg,0),
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

declare @result int
    , @objfso int
    , @drv int
    , @cdrive nvarchar(13)
    , @size nvarchar(50)
    , @free nvarchar(50)
    , @label nvarchar(10)
    , @dbs nvarchar(4000)
    , @logs nvarchar(4000)
    , @flds nvarchar(4000)
    , @oid int

select @flds=''
        letter nchar(1),
        total_mb bigint,
        free_mb bigint,
        label nvarchar(10),
        [% free] int,
        dbs nvarchar(4000),
        logs nvarchar(4000)
        '',
       @oid=object_id(''tempdb..#drive_info'')

-- ============================================================= check params ==
if charindex(''|run|'',@opt)=0 goto help

declare @sysdb table (db sysname)

create table #drive_space
    (
      letter nchar(1) not null,
      freemb nvarchar(10) not null
     )

if @oid is null
    begin
    create table #drive_info(id int identity)
    exec(''alter table #drive_info add ''+@flds)
    end

insert @sysdb select ''master'' union select ''msdb'' union select ''model''

insert into #drive_space
    exec master.dbo.xp_fixeddrives

-- iterate through drive letters.
declare  curdriveletters cursor local
    for select letter from #drive_space

declare @driveletter nchar(1)

open curdriveletters
fetch next from curdriveletters into @driveletter
while (@@fetch_status <> -1)
begin
    if (@@fetch_status <> -2)
    begin
    select @cdrive = ''getdrive("'' + @driveletter + ''")''
    exec @result = sp_oacreate ''scripting.filesystemobject'', @objfso output
    if @result = 0 exec @result = sp_oamethod @objfso, @cdrive, @drv output
    if @result = 0 exec @result = sp_oagetproperty @drv,''totalsize'', @size output
    if @result = 0 exec @result = sp_oagetproperty @drv,''freespace'', @free output
    if @result = 0 exec @result = sp_oagetproperty @drv,''volumename'', @label output
    if @result <> 0 exec sp_oadestroy @drv
    exec sp_oadestroy @objfso
    select @size = (convert(bigint,@size) / 1048576 )
    select @free = (convert(bigint,@free) / 1048576 )

    select @dbs=null
    select @dbs=isnull(@dbs+'' | '','''')+db_name(database_id)
    -- select *
    from sys.master_files
    where physical_name like @driveletter+'':%''
    and type=0 and not db_name(database_id) in (select db from @sysdb)

    select @logs=null
    select @logs=isnull(@logs+'' | '','''')+db_name(database_id)
    from sys.master_files
    where physical_name like @driveletter+'':%''
    and type=1 and not db_name(database_id) in (select db from @sysdb)

    insert into #drive_info(letter,total_mb,free_mb,label,dbs,logs)
        values (@driveletter, @size, @free, @label,@dbs,@logs)
    end
    fetch next from curdriveletters into @driveletter
end

close curdriveletters
deallocate curdriveletters

-- produce report.
update #drive_info set [% free]=cast(((convert(numeric(9,0),free_mb)
                               /convert(numeric(9,0),total_mb)) * 100) as int)

-- if @out is null select @sql='''' else select @sql =''insert ''+@out+'' ''
if @oid is null
    begin
    select * from #drive_info
    drop table #drive_info
    end

drop table #drive_space
goto ret

help:
exec sp__usage @proc,''
Scope
    list drives space info with list of dbs

See
    sp__util_vlf

Notes
    the table #drive_info is used return to caller
    the data

    create table #drive_info(%p1%)

Parameters
    @opt    options
            run     execute
    @dbg    not used
'',@p1=@flds

select @ret=-1

ret:
return @ret
end -- sp__util_drives'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_drives:

-- ============================================================= sp__util_gource
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_gource',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120823
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_gource') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_gource') with nowait
        goto skip_sp__util_gource
        end
    if @ver>120823
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_gource') with nowait
        goto skip_sp__util_gource
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_gource') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_gource'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_gource]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120823\s.zaglio: return a custom log for gource
    d:120823\s.zaglio: sp__script_svn_log
    t:sp__util_gource @opt=''run|util''
    t:sp__util_gource @opt=''run''
*/
CREATE proc sp__util_gource
    @opt sysname=null,
    @dbg int=0
as
begin
/*
gource.exe
--log-format svn -s 0.1 --stop-at-end --max-file-lag 10
--title "TSQL Utility" -640x480  --hide bloom,progress,dirnames
--disable-auto-rotate --key --file-idle-time 0 --camera-mode overview
utility.xml

ffmpeg
-y -r 60 -f image2pipe -vcodec ppm -i utility.ppm
-vcodec libx264 -preset ultrafast -crf 1 -threads 0 -bf 0 utility.avi
*/
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @opt is null or charindex(''|run|'',@opt)=0 goto help

-- ============================================================== declaration ==
declare
    @sdt sysname,@id int,@d sysname,@t sysname,@dt datetime,
    @util bit

-- insert before here --  @end_declare bit
create table #src(lno int identity, line nvarchar(4000))
-- =========================================================== initialization ==
select
    @util=charindex(''|util|'',@opt)
-- ======================================================== second params chk ==
-- ===================================================================== body ==
-- drop table #rel
select
    identity(int,1,1) as id,
    object_name(obj_id) name,
    case tag when ''d'' then cast(isnull(val3,val1) as sysname) else obj end as obj,
    tag,
    cast(val1 as sysname) as sdt,
    cast(val2 as sysname) as author
into #rel
-- select top 100 *
from fn__script_info(default,''rvdx'',default)
where not obj like ''%[_]old''

if @util=1 delete from #rel where not obj like ''%[_][_]%''
else delete from #rel where obj like ''%[_][_]%''

alter table #rel add
    dt datetime,
    unix_timestamp sysname,
    grp sysname,
    color int,
    scolor varchar(6)

update #rel set
    obj=case @util when 1
        then substring(obj,charindex(''__'',obj)+2,128)
        else obj
        end,
    grp=case @util when 1
        then dbo.fn__str_between(obj,''__'',''_'',default)
        else dbo.fn__str_between(obj,''_'',''_'',default)
        end,
    author=lower(isnull(author,
        case @util when 1 then ''s.zaglio'' else ''unknown'' end
        ))

update #rel set
    color=abs(dbo.fn__crc32(grp))/256

update #rel set grp=null,color=5592405*2.8 where grp=obj

update #rel set color=color+5592405 where color<5592405

update #rel set
    scolor=upper(right(dbo.fn__hex(color),6))

delete from #rel where isnumeric(sdt)=0 and tag=''d''

-- select * from #rel where obj is null or isnumeric(sdt)=0
-- select * from #rel where sdt like ''%24.100%''
-- set nocount on
-- declare @sdt sysname,@id int,@d sysname,@t sysname,@dt datetime
declare cs cursor local for
    select id,sdt
    from #rel
    where 1=1
open cs
while 1=1
    begin
    fetch next from cs into @id,@sdt
    if @@fetch_status!=0 break

    if @sdt=''000000'' select @sdt=convert(sysname,cast(0 as datetime),12)

    select
        @d= dbo.fn__format(left(@sdt,6),''@12-34-56'',default),
        @t= case when charindex(''.'',@sdt)=0
            then ''00:00:00''
            else dbo.fn__format(right(@sdt,4),''@12:34'',default)+'':00''
            end

    begin try
    select @dt=convert(datetime,@d,11)+convert(datetime,@t,8)
    update #rel set dt=@dt where id=@id
    end try
    begin catch
    exec sp__printf ''-- cannot convert %d:%s(%s,%s)'',@id,@sdt,@d,@t
    end catch

    end -- cursor cs
close cs
deallocate cs
-- select * from #rel where id in (379,487)
delete from #rel where dt is null

update #rel set unix_timestamp=dbo.fn__unix_timestamp(dt)

-- 1275543595|andrew|A|src/main.cpp|FF0000
select
    unix_timestamp + ''|'' +
    author + ''|'' +
    case tag when ''d'' then ''D'' else ''M'' end + ''|'' +
    isnull(grp+''/'','''')+obj + ''|'' +
    scolor
    as line
from #rel
order by dt

-- exec sp__print_table ''#src''

drop table #src

/* after generation, can use gourge. For me better results are with:

gource.exe utility.log -s 0.3 -r 25 --stop-at-end -800x600 --highlight-dirs
           --font-size 12 --font-colour 0000FF -o utility.ppm
           --key --title "utility" --max-files 0 --highlight-all-user -i 0

ffmpeg -y -b 3000K -r 30 -f image2pipe -vcodec ppm -i utility.ppm
       -vcodec libx264 -fpre "e:/share/ffmpeg/libx264-slow.ffpreset"
       -threads 0 utility.mp4
*/

goto ret

-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    produce a custom log to use with gsource from build info of sp/fn of db

Parameters
    @opt    options
            util    script only utility or sp/fn with __ in the name
            run     produce the log

Notes
    After that you can save the output to a file and call gource:

    gource.exe
        --log-format svn -s 0.1 --stop-at-end --max-file-lag 10
        --title "myTitle" -640x480  --hide bloom,progress,dirnames
        --disable-auto-rotate --key --file-idle-time 0 --camera-mode overview
        -o output.ppm
        output.log

    ffmpeg
        -y -r 60 -f image2pipe -vcodec ppm -i output.ppm
        -vcodec libx264 -preset ultrafast -crf 1 -threads 0 -bf 0 output.avi
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__util_gource'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_gource:

-- ========================================================== sp__util_instances
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_instances',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120112
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_instances') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_instances') with nowait
        goto skip_sp__util_instances
        end
    if @ver>120112
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_instances') with nowait
        goto skip_sp__util_instances
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_instances') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_instances'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_instances]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120112\s.zaglio: list infor about running instances
    c:
        originally from:
        http://social.msdn.microsoft.com/Forums/en/
             sqlsetupandupgrade/thread/fe689d83-0264-45d7-8d73-5b1ac43d09a6
*/
create proc sp__util_instances
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interfering with select statements.
set nocount on
-- if @dbg>=@@nestlevel exec sp__printf ''write here the error msg''
declare @proc sysname, @err int, @ret int -- standard API: 0=OK -1=HELP, any=error id
select  @proc=object_name(@@procid), @err=0, @ret=0, @dbg=isnull(@dbg,0)
select  @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
-- declare -- insert before here --  @end_declare bit
-- =========================================================== initialization ==
-- select -- insert before here --  @end_declare=1
-- ======================================================== second params chk ==
-- ===================================================================== body ==

Declare @CurrID int,@ExistValue int, @MaxID int, @SQL nvarchar(1000)
Declare @TCPPorts Table (PortType nvarchar(180), Port int)
Declare @SQLInstances Table (InstanceID int identity(1, 1) not null primary key,
                                          InstName nvarchar(180),
                                          Folder nvarchar(50),
                                          StaticPort int null,
                                          DynamicPort int null,
                                          Platform int null);
Declare @Plat Table (Id int,Name varchar(180),InternalValue varchar(50), Charactervalue varchar (50))
Declare @Platform varchar(100)
Insert into @Plat exec xp_msver platform
select @Platform = (select 1 from @plat where charactervalue like ''%86%'')
If @Platform is NULL
Begin
Insert Into @SQLInstances (InstName, Folder)
Exec xp_regenumvalues N''HKEY_LOCAL_MACHINE'',
                             N''SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL'';
Update @SQLInstances set Platform=64
End
else
Begin
Insert Into @SQLInstances (InstName, Folder)
Exec xp_regenumvalues N''HKEY_LOCAL_MACHINE'',
                             N''SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL'';
Update @SQLInstances Set Platform=32
End
Declare @Keyexist Table (Keyexist int)
Insert into @Keyexist
Exec xp_regread''HKEY_LOCAL_MACHINE'',
                              N''SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server\Instance Names\SQL'';
select @ExistValue= Keyexist from @Keyexist
If @ExistValue=1
Insert Into @SQLInstances (InstName, Folder)
Exec xp_regenumvalues N''HKEY_LOCAL_MACHINE'',
                              N''SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server\Instance Names\SQL'';
Update @SQLInstances Set Platform =32 where Platform is NULL
Select @MaxID = MAX(InstanceID), @CurrID = 1
From @SQLInstances
While @CurrID <= @MaxID
  Begin
      Delete From @TCPPorts
      Select @SQL = ''Exec xp_instance_regread N''''HKEY_LOCAL_MACHINE'''',
                              N''''SOFTWARE\Microsoft\\Microsoft SQL Server\'' + Folder + ''\MSSQLServer\SuperSocketNetLib\Tcp\IPAll'''',
                              N''''TCPDynamicPorts''''''
      From @SQLInstances
      Where InstanceID = @CurrID
      Insert Into @TCPPorts
      Exec sp_executesql @SQL
      Select @SQL = ''Exec xp_instance_regread N''''HKEY_LOCAL_MACHINE'''',
                              N''''SOFTWARE\Microsoft\\Microsoft SQL Server\'' + Folder + ''\MSSQLServer\SuperSocketNetLib\Tcp\IPAll'''',
                              N''''TCPPort''''''
      From @SQLInstances
      Where InstanceID = @CurrID

      Insert Into @TCPPorts
      Exec sp_executesql @SQL

      Select @SQL = ''Exec xp_instance_regread N''''HKEY_LOCAL_MACHINE'''',
                              N''''SOFTWARE\Wow6432Node\Microsoft\\Microsoft SQL Server\'' + Folder + ''\MSSQLServer\SuperSocketNetLib\Tcp\IPAll'''',
                              N''''TCPDynamicPorts''''''
      From @SQLInstances
      Where InstanceID = @CurrID

      Insert Into @TCPPorts
      Exec sp_executesql @SQL

      Select @SQL = ''Exec xp_instance_regread N''''HKEY_LOCAL_MACHINE'''',
                              N''''SOFTWARE\Wow6432Node\Microsoft\\Microsoft SQL Server\'' + Folder + ''\MSSQLServer\SuperSocketNetLib\Tcp\IPAll'''',
                              N''''TCPPort''''''
      From @SQLInstances
      Where InstanceID = @CurrID

      Insert Into @TCPPorts
      Exec sp_executesql @SQL

      Update SI
      Set StaticPort = P.Port,
            DynamicPort = DP.Port
      From @SQLInstances SI
      Inner Join @TCPPorts DP On DP.PortType = ''TCPDynamicPorts''
      Inner Join @TCPPorts P On P.PortType = ''TCPPort''
      Where InstanceID = @CurrID;
      Set @CurrID = @CurrID + 1
  End
Select serverproperty(''ComputerNamePhysicalNetBIOS'') as ServerName, InstName, StaticPort, DynamicPort,Platform
From @SQLInstances

-- goto ret

-- =================================================================== errors ==
-- err_sample1: exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param goto ret
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    list info about running instances

Parameters

Examples
''

-- select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc SP__UTIL_INSTANCES'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_instances:

-- ========================================================== sp__util_normalize
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_normalize',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120904
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_normalize') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_normalize') with nowait
        goto skip_sp__util_normalize
        end
    if @ver>120904
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_normalize') with nowait
        goto skip_sp__util_normalize
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_normalize') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_normalize'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_normalize]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:table,normalize,view
    TODO:create at least clustered index
    r:120904\s.zaglio: added distinct of distinct
    r:120829\s.zaglio: normalize a table creating a dictionary
    t:sp__util_normalize ''rep05_delivery_notes'',@opt=''overwrite'',@dbg=1
    t:exec sp__info_db ''rep05_delivery_notes%''
    t:select top 10 * from REP05_DELIVERY_NOTES_NORM
*/
CREATE proc sp__util_normalize
    @tbl sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare
    @proc sysname, @err int, @ret int -- @ret: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @tbl is null and @opt=''||'' goto help

-- ============================================================== declaration ==
declare
    @sql nvarchar(max),                 -- final sql to execute
    @flds nvarchar(max),                -- fields list
    @from nvarchar(max),                -- from clause
    @pk nvarchar(4000),                 -- pkey cols
    @tmp nvarchar(max),
    @id int,@i int,@n int,@top int,
    @qtbl sysname,                      -- quoted table name
    @ntbl sysname,                      -- normalized name
    @dict sysname, @dict_ix sysname,    -- dictionary tbl&idx
    @qdict sysname,
    @qnorm sysname,                     -- quoted normalized table name
    @qview sysname,                     -- quoted name view
    -- options
    @overwrite bit,
    --------
    @crlf nvarchar(2),
    @end_declare bit

create table #pk(
    id int identity,
    k1 sql_variant,k2 sql_variant null,
    k3 sql_variant null, k4 sql_variant null,
    grp int null,
    )
create index #ix_pk on #pk(grp)

-- =========================================================== initialization ==
select @id=object_id(@tbl)
select @tbl=name from sys.objects where object_id=@id -- real name

select
    @ntbl=@tbl,
    @qtbl=quotename(@tbl),
    @dict=@ntbl+''_DICT'',
    @dict_ix=''IX_''+@ntbl+''_DICT'',
    @qdict=quotename(@dict),
    @qnorm=quotename(@ntbl+''_NORM''),
    @qview=quotename(@ntbl+''_ALL''),

    -- options
    @overwrite=charindex(''|overwrite|'',@opt),
    --------
    @crlf=crlf,
    --------
    @end_declare=1
from fn__sym()

-- ======================================================== second params chk ==
if not exists(
    select top 1 * from sys.objects
    where object_id=@id and [type]=''U''
    ) goto err_tbl

-- ===================================================================== body ==

-- collect %varchar columns
select distinct
    c.name,
    right(''0000''+cast(c.column_id as sysname),4) cid,
    t.name as [type]
into #cols
-- select top 10 *
from sys.columns c
join sys.types t on c.system_type_id=t.user_type_id
where object_id=@id
-- select * from sys.types order by system_type_id

if @dbg>1 select * from #cols order by cid

-- ================================================================ get pkeys ==
select @top=dbo.fn__count(@tbl),@n=@top/65536
exec sp__printf ''-- %d records in %d chunks'',@i,@n
/*
select @pk=null,@i=1
select
    @pk=isnull(@pk+'','','''')+quotename(columnname),
    @tmp=isnull(@tmp+'','','''')+''K''+cast(@i as varchar),
    @i=@i+1
from fn__script_idx(@id)
where [primary]=1

select @sql=''insert #pk(''+@tmp+'') select ''+@pk+'' from ''+@qtbl
if @sql is null goto err_cod
if @dbg=1 exec sp__printsql @sql
exec(@sql)

update #pk set grp=id%65536
*/

-- goto master_tbl

-- ================================================== create dictionary table ==
-- drop view before because schemabind
if not object_id(@qview) is null exec(''drop view ''+@qview)

if not object_id(@dict) is null
    begin
    if @overwrite=1
        exec(''drop table ''+@qdict)
    else
        goto err_exs
    end

select @sql=''create table ''+@qdict+''(id int identity ''+
            ''constraint [PK''+@ntbl+''] primary key,txt nvarchar(4000))''
if @sql is null goto err_cod
if @dbg=1 exec sp__printsql @sql
exec(@sql)
select @sql=''create /*unique*/ index [''+@dict_ix+''] on ''+@qdict+''(txt)''
if @sql is null goto err_cod
if @dbg=1 exec sp__printsql @sql
exec(@sql)

-- for each varchar col, fill dictionary
select @sql=null
select @sql=isnull(@sql+'' union''+@crlf,'''')+
           +''select distinct [''+c.name+''] from ''+@qtbl
           +'' where not [''+c.name+''] is null''
from #cols c
where c.[type] like ''%varchar''
select @sql=''insert ''+@qdict+@crlf+@sql+@crlf

if @sql is null goto err_cod
if @dbg=1 exec sp__printsql @sql
exec(@sql)
if @@error!=0 goto err_cod

-- ======================================================== fill master table ==
master_tbl:

if not object_id(@qnorm) is null exec(''drop table ''+@qnorm)

-- select fld,d1.col_id as col_id,... from tbl join dict d1
select @flds=null
select @flds=isnull(@flds+'',''+@crlf,'''')
           +case
            when c.[type] like ''%varchar''
            then ''    D''+cid+''.ID as [''+c.name+''_DID]''
            else ''    TBL.''+c.name
            end
from #cols c
order by c.cid

select @from=''from ''+@qtbl+'' TBL with(nolock)''+@crlf
select @from=@from
            +''left join ''               -- left because can be null value
            +@qdict+'' as D''+cid+'' with (nolock) ''
            +'' on TBL.''+c.name+''=D''+cid+''.txt''+@crlf
from #cols c
where c.[type] like ''%varchar''
order by c.cid

-- create
select @sql=''select top 0''+@crlf+@flds+@crlf+''into ''+@qnorm+@crlf+@from
if @sql is null goto err_cod
if @dbg=1 exec sp__printsql @sql
exec(@sql)

-- insert
select @sql =''insert ''+@qnorm+@crlf
            +''select top ''+cast(@top as sysname)+@crlf   -- prevent bad joins
            +@flds+@crlf+@from
if @sql is null goto err_cod
if @dbg=1 exec sp__printsql @sql
exec(@sql)

-- ============================================================== create view ==
create_view:

-- hints and left join prevent create of indexes on view
select @from=''from dbo.''+@qnorm+'' TBL ''+@crlf
select @from=@from
            +''left join ''               -- left because can be null value
            +''dbo.''+@qdict+'' as D''+cid
            +'' on TBL.''+c.name+''_DID=D''+cid+''.id''+@crlf
from #cols c
where c.[type] like ''%varchar''
order by c.cid

select @flds=null
select @flds=isnull(@flds+'',''+@crlf,'''')
           +case
            when c.[type] like ''%varchar''
            then ''    D''+cid+''.TXT as [''+c.name+'']''
            else ''    TBL.''+c.name
            end
from #cols c
-- where not c.type like ''%text''    -- do not allow
-- and not c.type like ''%image''     -- clustered index
order by c.cid

select @sql =''create view ''+@qview+@crlf
            +''with schemabinding''+@crlf
            +''as''+@crlf
            +''select''+@crlf
            +@flds+@crlf
            +@from+@crlf
if @sql is null goto err_cod
if @dbg=1 exec sp__printsql @sql
exec(@sql)
-- ================================================================ add index ==
add_index:

goto ret

-- =================================================================== errors ==
err_tbl:
exec @ret=sp__err ''table not found or not a table'',@proc goto ret
err_exs:
exec @ret=sp__err ''destination exists; use OVERWRITE option'',@proc goto ret
err_cod:
exec @ret=sp__err ''inside sql code error'',@proc goto ret


-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    normalize a table creating a @tbl_DICT that contain all varchars
    and a @tbl_NORM that containt linked data
    and a @tbl_ALL that is a view that join NORM with DICT.

Parameters
    @tbl    is the name of table to normalize
    @opt    options
            overwrite   overwrite dict tables if exists

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__util_normalize'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_normalize:

-- ================================================================= sp__util_os
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_os',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120208
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_os') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_os') with nowait
        goto skip_sp__util_os
        end
    if @ver>120208
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_os') with nowait
        goto skip_sp__util_os
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_os') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_os'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_os]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:120208\s.zaglio: added code comment
    v:120126\s.zaglio: used cscript
    v:110125\s.zaglio: exp buf of cmdline to 256 instead of 128
    v:100919.1110\s.zaglio: a remake because outof return space (4000chars limit)
    v:100919.1100\s.zaglio: more info about processes
    v:100119\s.zaglio: int->bigint
    v:100105\s.zaglio: list windows processes
    t:sp__util_os list
*/
CREATE proc [dbo].[sp__util_os]
    @cmd sysname=null,
    @opt sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0

-- if @dbg=1 select *,dbo.fn__str_at(@cmd,'''',pos) at from fn__str_table(@cmd,'''')

if @cmd is null
or not dbo.fn__str_at(@cmd,'''',1) in (''kill'',''list'')
    goto help

select @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

declare @list table (pos int,token nvarchar(4000))
declare
    @i int,@n int,@line nvarchar(4000),
    @vbs nvarchar(4000),@sql nvarchar(4000),
    @tmp nvarchar(4000),@pid bigint,
    @csep nchar(1),@rsep nchar(1),
    @out bit,
    @p1 sysname

declare @stdout table (lno int identity,line nvarchar(4000))
create table #src(lno int identity,line nvarchar(4000))
create table #tpl(lno int identity primary key,line nvarchar(4000))

select
    @p1=isnull(dbo.fn__str_at(@cmd,'''',2),''''),
    @cmd=dbo.fn__str_at(@cmd,'''',1)

if @dbg=1 exec sp__printf ''cmd=%s   p1=%s'',@cmd,@p1

select @csep=''|'',@rsep=char(13)

if object_id(''tempdb..#osprocs'') is null
    begin
    create table #osprocs (
        pid bigint,
        [name] sysname,
        kb bigint,
        nWrites bigint,
        KTime bigint,
        UTime bigint,
        CmdLine nvarchar(1024)
        )
    select @out=1
    end
else
    select @out=0

/*
In alternative can use the WMIC utility
WMIC PROCESS get Caption,Commandline,Processid /Format:list
WMIC PROCESS where name="cmd.exe" get processid,commandline /format:list
WMIC PROCESS where processid=??? delete
*/

insert #tpl(line) select line from fn__ntext_to_lines(''
%kill%:
sub KillProcessTree(objWMIService,process)
''''wscript.echo process.name, process.handle,
''''"is a child of",process.parentprocessid
wql = "select * from win32_process " & _
      "where ParentProcessID=" & process.handle
set results = objWMIService.execquery(wql)
for each childProcess in results
KillProcessTree objWMIService,childProcess
next
process.terminate
end sub

strComputer = "."
strConn="winmgmts:"
''''strConn= _
''''    "winmgmts:" & _
''''    "{impersonationLevel=impersonate,(debug)}!\\" & _
''''    strComputer & "oot\cimv2" _

Set objWMIService = GetObject( strConn )

Set colProcessList = objWMIService.ExecQuery _
("Select * from Win32_Process Where handle = %pid%")

For Each objProcess in colProcessList
set rootProcess = objWMIService.get( _
    "win32_process.handle=" & objProcess.Handle _
    )
KillProcessTree objWMIService,rootProcess
Next
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
%list%:
Dim obj, obj1 , row , sql, cmdl
sql="SELECT " & _
    "name,WorkingSetSize,WriteOperationCount," & _
    "KernelModeTime,UserModeTime,CommandLine " & _
    "FROM Win32_Process " & _
    "WHERE name like ''''%name%%''''"
For Each obj In GetObject("winmgmts:").ExecQuery(sql)
    row = obj.handle & "|" & _
          obj.name & "|" & _
          obj.WorkingSetSize & "|" & _
          obj.WriteOperationCount & "|" & _
          obj.KernelModeTime & "|" & _
          obj.UserModeTime
    cmdl = obj.handle & "|" & obj.CommandLine
    Wscript.StdOut.WriteLine row
    Wscript.StdOut.WriteLine cmdl
Next
-- -- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --8<-- -- -- -- -- --
'',0)

if @cmd=''list''
    -- sp__util_os ''list cmd''
    -- sp__util_os list
    exec sp__script_template ''%list%'',@opt=''mix'',@tokens=''%name%'',@v1=@p1
if @cmd=''kill''
    begin
    -- sp__util_os ''kill 4984'',@dbg=1
    if isnumeric(@p1)=1
        exec sp__script_template ''%kill%'',@opt=''mix'',
                                 @tokens=''%pid%'',@v1=@p1
    else
        goto err_pid
    end -- kill

if @dbg=1 select * from #src

select
    @vbs=''%temp%\''+replace(cast(newid() as sysname),''-'',''_'')+''.vbs'',
    @line=''cscript //T:60 //Nologo ''+@vbs
    -- @line=''cscript ''+@vbs
exec sp__file_write_stream @vbs,@fmt=''ascii''

insert @stdout exec xp_cmdshell @line

select @line=''del ''+@vbs
exec xp_cmdshell @line,no_output

select @line=null

select top 1 @line=line
from @stdout
where line like ''%''+@vbs+''(%,%)%''
if not @line is null goto err_vbs

if @cmd=''kill'' goto ret

if @dbg=1
    select *
    from @stdout o
    cross apply fn__str_words(o.line,''|'',default)
    where o.lno%2=1

-- out objects cols order is different from select
insert #osprocs(pid,name,kb,nwrites,ktime,utime)
select
    convert(bigint,c00) as pid,
    convert(sysname,c01) as name,
    convert(bigint,c02)/1024 as kb,
    convert(bigint,c03) as nwrites,
    convert(bigint,c04) as ktime,
    convert(bigint,c05) as utime
from @stdout o
cross apply fn__str_words(o.line,''|'',default)
where o.lno%2=1

-- update with 2nd line : pid|cmdline
update p set
    cmdline=substring(o.line,charindex(''|'',o.line)+1,1024)
from #osprocs p
join @stdout o on p.pid=left(o.line,charindex(''|'',o.line)-1)
where o.lno%2=0

if @out=1
    begin
    select * from #osprocs
    where [name] like isnull(dbo.fn__str_at(@cmd,'''',2)+''%'',''%'')
    drop table #osprocs
    end
else
    delete from #osprocs
    where not [name] like isnull(dbo.fn__str_at(@cmd,'''',2)+''%'',''%'')

goto ret

-- =================================================================== errors ==
err_vbs:    exec @ret=sp__err ''%s'',@proc,@p1=@line goto ret
err_pid:    exec @ret=sp__err ''not numeric PID'',@proc goto ret
/*
class Win32_Process : CIM_Process
{
  string   Caption;
  string   CommandLine;
  string   CreationClassName;
  datetime CreationDate;
  string   CSCreationClassName;
  string   CSName;
  string   Description;
  string   ExecutablePath;
  uint16   ExecutionState;
  string   Handle;
  uint32   HandleCount;
  datetime InstallDate;
  uint64   KernelModeTime;
  uint32   MaximumWorkingSetSize;
  uint32   MinimumWorkingSetSize;
  string   Name;
  string   OSCreationClassName;
  string   OSName;
  uint64   OtherOperationCount;
  uint64   OtherTransferCount;
  uint32   PageFaults;
  uint32   PageFileUsage;
  uint32   ParentProcessId;
  uint32   PeakPageFileUsage;
  uint64   PeakVirtualSize;
  uint32   PeakWorkingSetSize;
  uint32   Priority;
  uint64   PrivatePageCount;
  uint32   ProcessId;
  uint32   QuotaNonPagedPoolUsage;
  uint32   QuotaPagedPoolUsage;
  uint32   QuotaPeakNonPagedPoolUsage;
  uint32   QuotaPeakPagedPoolUsage;
  uint64   ReadOperationCount;
  uint64   ReadTransferCount;
  uint32   SessionId;
  string   Status;
  datetime TerminationDate;
  uint32   ThreadCount;
  uint64   UserModeTime;
  uint64   VirtualSize;
  string   WindowsVersion;
  uint64   WorkingSetSize;
  uint64   WriteOperationCount;
  uint64   WriteTransferCount;
};
*/
help:
exec sp__usage @proc,''
Scope
    List OS processes

Parameters
    @cmd        commands and parameters
        kill    PID
        list    [process]
                list processes
                if exists #osprocs, return in this table the processes info

                create table #osprocs (
                    pid bigint,[name] sysname,
                    kb bigint, nWrites bigint,
                    KTime bigint,
                    UTime bigint,
                    CmdLine nvarchar(1024)
                    )

Examples
    sp__util_os list smss
''

select @ret=-1

ret:
return @ret
end -- sp__util_os'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_os:

-- ============================================================== sp__util_os_ps
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_os_ps',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=131209
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_os_ps') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_os_ps') with nowait
        goto skip_sp__util_os_ps
        end
    if @ver>131209
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_os_ps') with nowait
        goto skip_sp__util_os_ps
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_os_ps') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_os_ps'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_os_ps]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:process,status,list
    v:131209\s.zaglio: list os processes using wmi
    t:sp__util_os_ps ''%'',@dbg=1
*/
CREATE proc sp__util_os_ps
    @name sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
-- @@nestlevel is >1 if called by other sp (not correct if called by remote sp)

set nocount on

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    @err_msg nvarchar(2000)             -- used before raise

-- ======================================================== params set/adjust ==
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=nullif(@opt,''''),
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end
    -- @param=nullif(@param,''''),

-- ============================================================== declaration ==

declare
    @tmp nvarchar(512),
    @cmd nvarchar(4000),
    @txt nvarchar(max),
    @sql nvarchar(max),
    @cols sysname,
    @i int,
    @crlf nvarchar(2),
    @osprocs_id int

-- =========================================================== initialization ==

exec sp__get_temp_dir @tmp out,@opt=''tf''

select
    @tmp=@tmp+''.txt'',
    @crlf=crlf,
    @osprocs_id=isnull(object_id(''tempdb..#osprocs''),0)
from fn__sym()

-- ======================================================== second params chk ==

if @name is null goto help

-- =============================================================== #tbls init ==

declare @src table(lno int identity primary key,line nvarchar(4000))
declare @osprocs table(
    pid bigint, [name] sysname null,
    kb bigint null, nWrites bigint null,
    KTime bigint null, UTime bigint null,
    CmdLine nvarchar(1024) null
    )

-- ===================================================================== body ==

-- http://msdn.microsoft.com/en-us/library/aa394372(v=vs.85).aspx
-- get * or nothing for all
-- the list output follow the alphabetic order
select @cols=''Caption,Handle,CommandLine,Name,KernelModeTime,UserModeTime,''
            +''WorkingSetSize,WriteOperationCount''
select @cmd=''wmic /output:"''+@tmp+''" process get ''+@cols+'' /format:value''

if @dbg=0
    exec xp_cmdshell @cmd,no_output
else
    begin
    exec sp__printf ''%s'',@cmd
    exec xp_cmdshell @cmd
    end
select @sql=''select @txt=BulkColumn ''
           +''from openrowset(bulk ''''''+@tmp+'''''',  single_nclob) as x''
if @dbg>0 exec sp__printsql @sql
exec sp_executesql @sql,N''@txt nvarchar(max) out'',@txt=@txt out
if @dbg>0 exec sp__printsql @txt
select @cmd=''del /f /q "''+@tmp+''"''
exec xp_cmdshell @cmd,no_output

insert @src(line) select line from fn__ntext_to_lines(@txt,0)

-- t:sp__util_os_ps ''%'',@dbg=1

;with
var_val as (
    select
        lno,
        left(line,charindex(''='',line)-1) as [var],
        substring(line,charindex(''='',line)+1,len(line)) as [val]
    from @src
    where line!=''''
),
hdr as (
    select
        row_number() over(partition by 1 order by lno) as row,*
    from var_val
    where [var]=''caption''
),
maxlno as (
    select max(lno) as mlno from @src
),
range as (
    select
        row_number() over(partition by 1 order by (select 1)) as gid,
        h1.lno llno,isnull(h2.lno-1,mlno) rlno
    from maxlno,hdr h1
    left join hdr h2
    on h1.row+1=h2.row
    ),
vals as (
    select b.gid,a.*
    from var_val a
    join range b on a.lno between b.llno and b.rlno
    ),
Handle as (
    select gid,val as handle
    from vals
    where [var]=''handle''
    ),
Name as (
    select gid,val as Name
    from vals
    where [var]=''name''
    ),
CommandLine as (
    select gid,left(val,1024) as CommandLine
    from vals
    where [var]=''CommandLine''
    ),
KernelModeTime as (
    select gid,val as KernelModeTime
    from vals
    where [var]=''KernelModeTime''
    ),
UserModeTime as (
    select gid,val as UserModeTime
    from vals
    where [var]=''UserModeTime''
    ),
WorkingSetSize as (
    select gid,val as WorkingSetSize
    from vals
    where [var]=''WorkingSetSize''
    ),
WriteOperationCount as (
    select gid,val as WriteOperationCount
    from vals
    where [var]=''WriteOperationCount''
    )
insert @osprocs(
    pid,name,cmdline,
    KTime,UTime,
    kb,nWrites
)
select
    handle,name,commandline,
    KernelModeTime,UserModeTime,
    WorkingSetSize,WriteOperationCount
from handle
join name on handle.gid=name.gid
join commandline on handle.gid=commandline.gid
join KernelModeTime on handle.gid=KernelModeTime.gid
join UserModeTime on handle.gid=UserModeTime.gid
join WorkingSetSize on handle.gid=WorkingSetSize.gid
join WriteOperationCount on handle.gid=WriteOperationCount.gid
where name like @name

if @osprocs_id=0
    select * from @osprocs
else
    insert #osprocs select * from @osprocs

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    List OS processes with info.

Notes
    - called by sp__util_os
    - maybe compatible with all present and future version of Windows
      starting from XP (probably SP 3 and above)

Parameters
    [param]     [desc]
    @name       like expression over name
    #osprocs    returned table if exists
                create table #osprocs (
                    pid bigint,[name] sysname,
                    kb bigint, nWrites bigint,
                    KTime bigint,
                    UTime bigint,
                    CmdLine nvarchar(1024)
                    )
    @opt        options
    @dbg        1=last most importanti info/show code without execute it
                2=more up level details
                3=more up ...

Examples
    sp__util_os_ps "%"
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
-- if @@trancount > 0 rollback -- for nested trans see style "procwnt"

exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch   -- proc sp__util_os_ps'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_os_ps:

-- ========================================================== sp__util_sp2dotnet
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_sp2dotnet',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110326
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_sp2dotnet') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_sp2dotnet') with nowait
        goto skip_sp__util_sp2dotnet
        end
    if @ver>110326
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_sp2dotnet') with nowait
        goto skip_sp__util_sp2dotnet
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_sp2dotnet') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_sp2dotnet'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_sp2dotnet]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g: script,utility
    v:110326\s.zaglio: added parameters clear
    v:110305\s.zaglio: done first version
    r:100127\s.zaglio: create a vb function to call a sp
    t:sp__util_sp2dotnet ''sp__util_sp2dotnet'',@opt=''class''
*/
CREATE proc [dbo].[sp__util_sp2dotnet]
    @obj sysname=null,
    @base sysname=null out,
    @lng sysname=null,
    @opt sysname=null,
    @dbg int=null
as
begin
set nocount on
declare @proc sysname,@ret int,@err int
select @proc=object_name(@@procid),@ret=0,@dbg=isnull(@dbg,0)
select @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
if @obj is null and object_id(''tempdb..#objs'') is null goto help

select
    @lng=isnull(@lng,''vbnet''),
    @base=isnull(@base,''db'')

if not @lng in (''vbnet'') goto help

-- ============================================================= declarations ==
declare
    @xtype nvarchar(2),@tab1 sysname,@tab2 sysname,@tab3 sysname,@id int

if object_id(''tempdb..#objs'') is null
    create table #objs(obj sysname,xtype nvarchar(2) null)

create table #src(lno int,line nvarchar(4000))
create table #cols(
    id int identity,
    col sysname, xtype sysname,
    ln sysname,prec sysname,scale sysname,
    nullable bit,isout bit,
    vbtype sysname null
    )

-- =========================================================== initialization ==
select
    @tab1=''    '',@tab2=@tab1+@tab1,@tab3=@tab2+@tab1

if not @obj is null
    insert #objs(obj,xtype)
    select [name],xtype from sysobjects
    where [name] like @obj

-- ===================================================================== body ==
-- add header
if charindex(''|class|'',@opt)>0
    begin
    insert into #src(line) select '''''' v:''+convert(sysname,getdate(),12)+''.''
                                         +left(replace(convert(sysname,getdate(),8),'':'',''''),4)
                                         +''\sp__util_sp2dotnet: generated automatically''
    insert into #src(line) select ''Imports System''
    insert into #src(line) select ''Imports System.Data''
    insert into #src(line) select ''''
    insert into #src(line) select ''Public Class ''+@base+''_objs''
    insert into #src(line) select @tab1+''Inherits ''+@base
    insert into #src(line) select '''''' inherited by ''+@base+''_io''
    insert into #src(line) select ''''
    insert into #src(line) select @tab1+''Public Sub New(Optional ByVal cs As String = "")''
    insert into #src(line) select @tab2+''MyBase.New(cs)''
    insert into #src(line) select @tab1+''End Sub''
    insert into #src(line) select ''''
    end -- class

-- add stored
declare cs cursor local for
    select obj,xtype,object_id(obj)
    from #objs
    where 1=1
open cs
while 1=1
    begin
    fetch next from cs into @obj,@xtype,@id
    if @@fetch_status!=0 break

    if @xtype!=''P''
        begin
        exec sp__printf ''%s excluded because not managed type(%s)'',@obj,@xtype
        continue
        end

    truncate table #cols
    insert #cols(col,xtype,ln,prec,scale,nullable,isout,vbtype)
    -- declare @id int select @id=object_id(''swl_rfc'')
    select
        -- select * from systypes
        c.[name],case when t.xusertype=256 then ''nvarchar'' else t.[name] end ,

        case -- select * from systypes
        when c.xusertype in (175,239,231,167,256) --char,nchar,nvc,vc
        then c.prec
        else c.length
        end as [length],

        case -- select * from systypes
        when c.xusertype in (175,239,231,167,256) --char,nchar,nvc,vc
        then 0
        else c.prec
        end as prec,

        isnull(convert(sysname,c.scale),''nothing'') as scale,
        c.isnullable,c.isoutparam,
        case -- select * from systypes
        when c.xusertype in (175,239,231,167,256) --char,nchar,nvc,vc
        then ''string'' -- +''(''+convert(sysname,prec)+'')''
        when c.xusertype in (62,106,108) then ''double'' -- float,decimal,numeric
        when c.xusertype in (59) then ''single'' -- real
        when c.xusertype in (61,58) then ''date'' -- *datetime
        when c.xusertype in (56) then ''int32'' -- int
        when c.xusertype in (52) then ''int16'' -- smallint
        when c.xusertype in (48) then ''byte'' -- tinyint
        when c.xusertype in (60) then ''currency'' -- money
        when c.xusertype in (35,99) then ''string'' -- text,ntext
        when c.xusertype in (104) then ''boolean'' -- bit
        when c.xusertype in (98) then ''object'' -- sql_variant
        else ''object ''''unk type''
        end as vbtype
    from syscolumns c
    join systypes t on c.xusertype=t.xusertype
    where c.id=@id
    order by colid

    insert into #src(line) select @tab1+''public function [''+@obj+'']( _''

    insert into #src(line)
    -- select * from systypes
    -- select * from syscolumns c where id=object_id('''')
    select @tab3+
        case when nullable =1 then ''optional '' else '''' end+
        case isout when 1 then ''byref '' else ''byval '' end+
        substring(col,2,4000)+'' as ''+
        vbtype+
        case when nullable=1 then ''=nothing'' else '''' end+
        '', _''
    from #cols
    order by id

    insert into #src(line) select @tab3+''optional byref raiserrorIfRetNotIs as integer=0, _''
    insert into #src(line) select @tab3+''optional byval tst_params as boolean=false _''
    insert into #src(line) select @tab2+'') as dataset''
    insert into #src(line) select ''''
    insert into #src(line) select @tab1+''Dim ex As Exception = Nothing''
    insert into #src(line) select @tab1+''Dim retval as integer,da as SqlClient.SqlDataAdapter=nothing''
    insert into #src(line) select @tab1+''Dim ds as system.Data.DataSet=nothing''
    insert into #src(line) select @tab1+''Dim p As system.Data.SqlClient.SqlParameter''
    insert into #src(line) select ''''
    insert into #src(line) select @tab1+''p=New system.Data.SqlClient.SqlParameter("retvalue", SqlDbType.Int)''
    insert into #src(line) select @tab1+''p.Direction = ParameterDirection.ReturnValue''
    insert into #src(line) select @tab1+''cmd.Parameters.Clear()''
    insert into #src(line) select @tab1+''cmd.parameters.add(p)''

    insert into #src(line)
    select top 100 percent
        ''cmd.parameters.add(New system.Data.SqlClient.SqlParameter(''+
        ''"''+col+''",SqlDbType.''+xtype+'',''+
        ln+'',''+
        case
            when isout=1 then ''ParameterDirection.InputOutput''
            else ''ParameterDirection.Input''
        end+'',''+
        case when nullable=1 then ''true,'' else ''false,'' end+
        prec+'',''+
        scale+'',''+
        ''nothing,DataRowVersion.Current,''+
        substring(col,2,4000)
        +''))''
        as line
    from #cols
    order by id

    insert into #src(line) select @tab1+''cmd.CommandType = System.Data.CommandType.StoredProcedure''
    insert into #src(line) select @tab1+''cmd.CommandText="''+@obj+''"''
    insert into #src(line) select @tab1+''debug(cmd)''
    -- .Parameters("au_id").Value = "172-32-1176"
    insert into #src(line) select @tab1+''da = New SqlClient.SqlDataAdapter(cmd)''
    insert into #src(line) select @tab1+''ds = New system.Data.DataSet''
    insert into #src(line) select @tab1+''timer.Start()''
    insert into #src(line) select @tab1+''try''
    insert into #src(line) select @tab1+''da.Fill(ds)''
    insert into #src(line) select @tab1+''catch local_ex As Exception''
    insert into #src(line) select @tab3+''ex=local_ex''
    insert into #src(line) select @tab1+''end try''
    insert into #src(line) select @tab1+''cmd.CommandType = System.Data.CommandType.Text''
    insert into #src(line) select @tab1+''timer.Stop()''
    insert into #src(line) select @tab1+''da.Dispose()''
    insert into #src(line) select @tab1+''da = Nothing''
    insert into #src(line) select @tab3+''if not ex is nothing then throw ex''

    insert into #src(line) select @tab1+''retval=nz(CType(cmd.Parameters("retvalue").Value, Integer),0)''
    insert into #src(line) select @tab1+''if retval<>raiserrorIfRetNotIs then''
    insert into #src(line) select @tab3+''throw new exception("error(" & retval & ") in ""'' + @obj + ''""")''
    insert into #src(line) select @tab1+''else''
    insert into #src(line) select @tab3+''raiserrorIfRetNotIs = retval''
    insert into #src(line) select @tab1+''end if ''

    -- output values
    insert into #src(line)
    select top 100 percent
        @tab1+substring(col,2,4000)+''=CType(cmd.Parameters("''+col+''").Value, ''+vbtype+'')''
        as line
    from #cols
    where isout=1
    order by id

    insert into #src(line) select @tab1+''return ds''
    insert into #src(line) select @tab1+''end function '''' ''+@obj
    end -- while of cursor
close cs
deallocate cs

if charindex(''|class|'',@opt)>0
    begin
    insert into #src(line) select ''''
    insert into #src(line) select ''End Class '''' ''+@base+''_objs''
    end -- class

select line from #src order by lno
drop table #src
drop table #cols

goto ret
-- =================================================================== errors ==

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    generate the dotnet code necessary to use the objects of db

Parameters
    @obj    object name, can use wild %
            can pass directly #objs as
                create table #objs(obj sysname,xtype nvarchar(2) null)
    @base   is the base class used with "class" option; by default is "db"
    @lng    specify .net language of output
            possible languages are:
            null    (default)
            vbnet   (default)
    @opt    options
            class       enclose functions into class db_io inherited from db
            svn         TODO:out to svn (see sp__svn)
''

ret:
return @ret
end -- [sp__util_sp2dotnet]'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_sp2dotnet:

-- ========================================================== sp__util_sqlrecent
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_sqlrecent',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120731.1000
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_sqlrecent') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_sqlrecent') with nowait
        goto skip_sp__util_sqlrecent
        end
    if @ver>120731.1000
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_sqlrecent') with nowait
        goto skip_sp__util_sqlrecent
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_sqlrecent') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_sqlrecent'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_sqlrecent]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:perf,sql
    r:120731.1000\s.zaglio: show recent query
*/
CREATE proc sp__util_sqlrecent
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare
    @proc sysname, @err int, @ret int -- @ret: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
-- if @opt=''||'' goto help

-- ============================================================== declaration ==
-- declare -- insert before here --  @end_declare bit
-- =========================================================== initialization ==
-- select -- insert before here --  @end_declare=1
-- ======================================================== second params chk ==
-- ===================================================================== body ==

select distinct
    db_name(dbid) db,
    object_name(objectid,dbid) obj,
    deqs.last_execution_time as [time],
    last_worker_time,min_worker_time,max_worker_time,
    dest.text as [query]
    -- ,substring(dest.text,statement_start_offset,case statement_end_offset when -1 then len(dest.text) else statement_end_offset end) stmnt
    -- ,*
from sys.dm_exec_query_stats as deqs
cross apply sys.dm_exec_sql_text(deqs.sql_handle) as dest
order by deqs.last_execution_time desc

goto help
goto ret

-- =================================================================== errors ==
/*
err_sample1:
exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param
goto ret
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    list lrecent queryes with times

Parameters
    [param]     [desc]

Examples
    [example]
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__util_sqlrecent'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_sqlrecent:

-- ============================================================= sp__util_status
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_status',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100328
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_status') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_status') with nowait
        goto skip_sp__util_status
        end
    if @ver>100328
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_status') with nowait
        goto skip_sp__util_status
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_status') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_status'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_status]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:100328\s.zaglio: added help
    v:100212\s.zaglio: added drives free space inspect
    v:100131\s.zaglio: save and compare mssql configuration
    t:sp__util_status @reset=1,@dbg=1
    t:
        declare @r int
        exec @r=[sp__util_status] @dbg=1
        print @r
        select * from TBL_SAVED_CONFIGURATION where tid=''dri''
*/
CREATE proc [dbo].[sp__util_status]
    @reset bit=0,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@sql nvarchar(4000),@dt datetime,@i int,@n int,@j int,@r int
declare @first datetime,@last datetime

select
    @proc=''sp__util_status'',
    @dt=getdate(),
    @r=1

-- drop table TBL_SAVED_CONFIGURATION
if dbo.fn__exists(''TBL_SAVED_CONFIGURATION'',''u'')=0
    begin
    -- declare @proc sysname,@sql nvarchar(4000),@dt datetime,@i int,@n int
    select @sql=''create table TBL_SAVED_CONFIGURATION (\n''
               +''\ttid char(4),\n\thdr bit default 0,id int identity,\n\tdt datetime''
    select @i=1,@n=30
    while @i<=@n select @sql=@sql+'',\n\tv''+convert(sysname,@i)+'' sysname null'',@i=@i+1
    select @sql=@sql+'')''
    select @sql=replace(@sql,''\n'',char(13))
    select @sql=replace(@sql,''\t'',''    '')
    exec sp__printf ''-- creating table ''
    exec(@sql)
    select @reset=1
    end

-- delete last registration
select @first=min(dt),@last=max(dt) from TBL_SAVED_CONFIGURATION
if @first!=@last delete from TBL_SAVED_CONFIGURATION where dt=@last

create table #sp_configure (
    name sysname,
    minimum int,
    maximum int,
    config_value int,
    run_value int)

insert into #sp_configure exec sp_configure

-- select * from #sp_configure
select * into #databases from master..sysdatabases with (nolock)

select * into #servers from master..sysservers with (nolock)

create table #drives(name sysname,freeperc int)
exec sp__util_drives ''letter,pc'',''#drives'',@dbg=1

if @reset=1
    begin
    exec sp__printf ''-- 1st registration''
    truncate table TBL_SAVED_CONFIGURATION
    end

-- declare @j int,@i int,@n int,@sql nvarchar(4000),@dt datetime select @dt=getdate()
declare @flds nvarchar(4000),@tbls sysname,@tbl sysname,@tid sysname
select @tbls=''#sp_configure:cfg,#databases:dbs,#servers:svrs,#drives:dri''
select @j=dbo.fn__str_count(@tbls,'','')

while @j>0
    begin
    select @tbl=dbo.fn__str_at(@tbls,'','',@j)
    select @tid=dbo.fn__str_at(@tbl,'':'',2)
    select @tbl=dbo.fn__str_at(@tbl,'':'',1),@j=@j-1
    exec sp__printf ''-- saving %s'',@tbl

    select @flds=dbo.fn__flds_of(@tbl,'','',null)
    if @dbg=1 exec sp__printf ''@tbl=%s, @flds=%s'',@tbl,@flds

    select @i=1,@n=dbo.fn__str_count(@flds,'','')
    if @n>30 select @n=30
    select @sql=''insert into TBL_SAVED_CONFIGURATION (tid,hdr,dt''
    while @i<=@n select @sql=@sql+'',v''+convert(sysname,@i),@i=@i+1
    select @sql =@sql+'')\nselect ''''''+@tid+'''''',1,''''''+convert(sysname,@dt,126)+'''''',''
                +dbo.fn__str_exp(''''''%%'''''',@flds,'','')
    select @sql=replace(@sql,''\n'',char(13))
    if @dbg=1 exec sp__printf @sql
    exec(@sql)

    select @i=1,@n=dbo.fn__str_count(@flds,'','')
    if @n>30 select @n=30
    select @sql=''insert into TBL_SAVED_CONFIGURATION (tid,dt''
    while @i<=@n select @sql=@sql+'',v''+convert(sysname,@i),@i=@i+1
    select @sql =@sql+'')\nselect ''''''+@tid+'''''',''''''+convert(sysname,@dt,126)+'''''',''
                +dbo.fn__str_exp(''convert(sysname,%%)'',@flds,'','')
    select @sql=@sql+''from [''+@tbl+'']''
    select @sql=replace(@sql,''\n'',char(13))
    if @dbg=1 exec sp__printf @sql
    exec(@sql)
    end -- while

if @reset=0
    begin
    select @first=min(dt),@last=max(dt) from TBL_SAVED_CONFIGURATION
    select @sql=''select isnull(a.tid,b.tid) tid,isnull(a.v1,b.v1) v1_key''
    select @i=2,@n=30
    while (@i<=@n)
        select @sql=@sql+'',a.v''+convert(sysname,@i)+'',b.v''+convert(sysname,@i),@i=@i+1

    select @sql=@sql+''
        from TBL_SAVED_CONFIGURATION a
        full outer join TBL_SAVED_CONFIGURATION b
        on a.tid=b.tid and a.v1=b.v1
        where a.dt=''''''+convert(sysname,@first,126)+'''''' and b.dt=''''''+convert(sysname,@last,126)+''''''
        and (a.hdr=1''
    select @i=2
    select @sql=@sql+'' or 1=case when a.tid=''''dri'''' and convert(int,b.v2)<11 then 1 when a.tid!=''''dri'''' and (1=0''
    while (@i<=@n)
        select @sql=@sql+'' or isnull(a.v''+convert(sysname,@i)
                   +'','''''''')!=isnull(b.v''+convert(sysname,@i)+'','''''''')'',@i=@i+1

    select @sql=@sql+'') then 1 else 0 end)
        order by a.id''

    if @dbg=1 exec sp__printf @sql
    select @n=count(*)/2 from TBL_SAVED_CONFIGURATION where hdr=1
    exec(@sql)
    select @i=@@rowcount,@r=@@error
    if @i!=@n and @r=0 select @r=1 else select @r=0

    end -- compare

-- select * from TBL_SAVED_CONFIGURATION order by tid,id
-- update TBL_SAVED_CONFIGURATION set v2=v2+1 where id=54
drop table #sp_configure
drop table #databases
drop table #servers
drop table #drives

exec sp__usage @proc

return @r
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_status:

-- ========================================================== sp__util_tounicode
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_tounicode',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091018
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_tounicode') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_tounicode') with nowait
        goto skip_sp__util_tounicode
        end
    if @ver>091018
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_tounicode') with nowait
        goto skip_sp__util_tounicode
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_tounicode') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_tounicode'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_tounicode]

begin try
exec dbo.sp_executesql @statement = N'/*  keep this for MS compatibility
    l:see LICENSE file
    g:utility
    v:091018\s.zaglio:  mark objs that use datalength and exclusions of utils and temps
    v:090915\s.zaglio:  final tuning
    r:090914\s.zaglio:  some simple bugs
    r:090911\s.zaglio:  drops from together to one to one
    r:090910\s.zaglio:  revision to generate instead of mange directly
    r:090813.1700\s.zaglio:  generate script to trasform the db to unicode
    t:sp__util_tounicode @skip_code=1
*/
CREATE proc [dbo].[sp__util_tounicode]
    @uid sysname=''sa'',
    @pwd sysname='''',
    @skip_code bit=0,
    @skip_scripts bit=0,
    @dbg bit=0
as
begin
set nocount on
/*
    Due use of not logical names we must use this approch:
    .
    .  begin transaction
    .  drop sp,fn,vi
    .  script upsized to unicode
    .  commit
    .
    .  drop fkeys
    .  for each table with varchar,char,text
    .    count rows
    .    make a backup
    .    count backup rows
    .    script table
    .    convert to unicode
    .    drop original
    .    recreate unicoded table
    .    disable eventual identity
    .    reinsert data
    .    compare rows and if eq drop temp data
    .    run script dri
    .
    .  run script fk
    .
    .  remove old scrips
*/
-- select distinct xtype from sysobjects
-- test per fkey
-- sp_fkeys ''am01_us''  -- sp_find ''FK_AM03_MM_FUNC_AM01_US''
-- select * from sysobjects where parent_obj=object_id(''am01_us'')
-- select * from sysobjects where parent_obj=object_id(''AM03_MM_FUNC'')
-- select * from sysindexes where id=object_id(''am01_us'')
-- select * from sysforeignkeys where rkeyid=object_id(''am01_us'')
-- select * from sysobjects where id=623341285  -- print object_name(313768175) -->AM03_MM_FUNC
-- drop index AM03_MM_FUNC.FK_AM03_MM_FUNC_AM01_US
-- test normale
-- select * from sysobjects where parent_obj=object_id(''ot04_stock'')
-- select * from sysindexes where id=object_id(''ot04_stock'')

declare
    @proc sysname,
    @i int,@n int,@j int,@step int,@k int,
    @obj sysname,@new sysname,@msg nvarchar(4000),
    @sql  nvarchar(4000), @flds nvarchar(4000),
    @xtype nvarchar(2),
    @oc_tbl smallint,
    @oc_dri smallint,
    @oc_trg smallint,
    @oc_prp smallint,
    @oc_fk smallint,
    @cur_db sysname,@cur_srv sysname,
    @ret int,@crlf nchar(2),
    @upsize_marker sysname,
    @timer datetime,
    @tmp sysname,
    @file sysname,
    @cmd nvarchar(512),
    @parent_obj int

select
    @proc=''sp__util_tounicode'',
    @oc_tbl=1,   -- table with owner and not chk
    @oc_dri=20,  -- pkey,idx and chk
    @oc_trg=32,  -- triggers only
    @oc_prp=128, -- ex props
    @oc_fk=1024, -- only fkeys
    @cur_db=db_name(),
    @cur_srv=@@servername,
    @ret=0, @crlf=nchar(13)+nchar(10),
    @upsize_marker=''-- saved:''+convert(sysname,getdate(),126)

exec sp__printf ''-- @cur_db=%s, @spid=%d, @procid=%d'',@cur_db,@@spid,@@procid

create table #objs(id int identity(1,1),obj sysname,xtype nvarchar(2),parent_obj int) -- sp,fn,vi

declare @xt_vchar int,@xt_char int,@xt_text int
select @xt_vchar=xtype from systypes where name=''varchar''
select @xt_char=xtype  from systypes where name=''char''
select @xt_text=xtype  from systypes where name=''text''

select @step=10

-- temp table
create table #src   (lno int identity(10,10),line nvarchar(4000))
create table #dri   (lno int identity(10,10),line nvarchar(4000))

/*
create table @exclude (id int identity,obj sysname)
insert into @exclude(obj) select token from dbo.fn__str_table(@excludes,''|'')
select @i=min(id),@n=max(id) from @exclude
while (@i<=@n)
    begin
    select @obj=obj from @exclude where id=@i
    if charindex(''%'',@
    end
*/

/*
exec sp__drop ''tmp_*'',@simul=0
*/

exec sp__elapsed @timer out,''-- script generation started at:''

-- select sp,fn,vi that have varchar,char,text
-- select top 1 * from sysusers
insert into #objs(obj,xtype,parent_obj)
select quotename(u.name)+''.''+quotename(o.name),o.xtype,o.parent_obj
from sysobjects o join sysusers u on o.uid=u.uid
where o.xtype in (''P'',''FN'',''IF'',''TF'',''V'')
and (exists(
        select null from syscolumns c
        where c.id=o.id and c.xtype in (@xt_vchar,@xt_char,@xt_text)
        )
    or exists(
        select null from syscomments c
        where c.id=o.id
        and (
            charindex(''varchar'',[text])=1
            or charindex(''char'',[text])=1
            or charindex(''text'',[text])=1
            or charindex(''[varchar'',[text])>0
            or charindex(''[char'',[text])>0
            or charindex(''[text'',[text])>0
            or charindex('' varchar'',[text])>0
            or charindex('' char'',[text])>0
            or charindex('' text'',[text])>0
            )
        )
    )
and not (
    o.name in (''dtproperties'',@proc)
    or o.name like ''dt[_]%''
    or o.name like ''sys%''
    or o.name like ''tmp[_]%''
    or o.name like ''[sfv][pni][__]%'' -- sp__,fn__,vi__
    or o.name like ''%[_]tmp''
    or o.name like ''%[_]temp''
    or o.name like ''%[_]tmp''
    or o.name like ''%[_]backup''
    or o.name like ''%[_]test''
    or o.name like ''%[_][0-9][0-9][0-9][0-9][0-9][0-9][_][0-9][0-9][0-9][0-9]'' -- *_AAMMGG_HHMM
    )
order by xtype,o.name

-- select tables that have varchar,char,text
-- select * from sysforeignkeys
insert into #objs(obj,xtype,parent_obj)
select quotename(u.name)+''.''+quotename(o.name),o.xtype,o.parent_obj -- case when o.xtype=''F'' then f.rkeyid else o.parent_obj end
from sysobjects o join sysusers u on o.uid=u.uid
-- left join sysforeignkeys f
-- on o.id=f.constid
where o.xtype=''F''  -- foreignkeys
or (o.xtype=''U''
    and (exists(
            select null from syscolumns c
            where c.id=o.id and c.xtype in (@xt_vchar,@xt_char,@xt_text)
            )
        or exists(
            select null from syscomments c
            where c.id=o.id
            and (
                charindex(''varchar'',[text])=1
                or charindex(''char'',[text])=1
                or charindex(''text'',[text])=1
                or charindex(''[varchar'',[text])>0
                or charindex(''[char'',[text])>0
                or charindex(''[text'',[text])>0
                or charindex('' varchar'',[text])>0
                or charindex('' char'',[text])>0
                or charindex('' text'',[text])>0
                )
            )
        )
    and not (
            o.name in (''dtproperties'',@proc)
            or o.name like ''dt[_]%''
            or o.name like ''sys%''
            or o.name like ''tmp[_]%''
            or o.name like ''[sfv][pni][__]%'' -- sp__,fn__,vi__
            or o.name like ''%[_]tmp''
            or o.name like ''%[_]temp''
            or o.name like ''%[_]tmp''
            or o.name like ''%[_]backup''
            or o.name like ''%[_]test''
            or o.name like ''%[_][0-9][0-9][0-9][0-9][0-9][0-9][_][0-9][0-9][0-9][0-9]'' -- *_AAMMGG_HHMM
        )
    )
order by xtype,o.name


-- save old scrips
select @n=count(*) from #objs
if @n>0 exec sp__elapsed @timer out,''-- found %d objects to script in:'',@v1=@n
else goto err_noobjs

exec sp__printf ''-- NB: if occur an error copy and paste code into a new windows to not loose info''
exec sp__printf ''-- NB: search for "keepeye" notes before run scrit''
exec sp__printf ''-- prevent from a too fast F5 running''
exec sp__printf ''declare @secure bit select @secure=1 ''
exec sp__printf ''if @secure=1 raiserror(''''Disable @secure first'''',20,1) with log ''
exec sp__printf ''GO''

exec sp__printf ''-- goto single user mode''
exec sp__printf ''USE [master] ''
exec sp__printf ''ALTER DATABASE [%s] SET  SINGLE_USER WITH ROLLBACK IMMEDIATE'',@cur_db
exec sp__printf ''ALTER DATABASE [%s] SET  SINGLE_USER '',@cur_db
exec sp__printf ''USE [%s] '',@cur_db
exec sp__printf ''GO''

exec sp__printf ''-- ### scripts objects to convert and save to scripts_...: (%t) ### '',@force=0

-- convert code
if @skip_code=1 goto skip_code

exec sp__printf ''set nocount on'',@force=0
exec sp__printf ''set transaction isolation level serializable'',@force=0

select @obj=null,@i=min(id),@n=max(id) from #objs
while (@i<=@n)
    begin
    select @obj=obj,@xtype=xtype from #objs where id=@i
    select @i=@i+1
    if @xtype in (''U'',''F'',''TR'') or @obj in (''sp__script'',''sp__script_reduce'') continue

    truncate table #src
    insert #src(line) select ''GO''
    -- insert #src(line) select ''begin tran''
    insert #src(line) select ''raiserror(''''Dropping & recreating ''+@obj+''...'''',10,1)''
    select @sql =''drop ''
                +case
                    when @xtype in (''P'') then ''proc ''
                    when @xtype in (''FN'',''IF'',''TF'') then ''function ''
                    when @xtype in (''V'') then ''view ''
                    when @xtype in (''U'') then ''table ''
                 end
                +@obj
    insert #src(line) select @sql
    insert #src(line) select ''GO''
    exec sp__script @obj,''#src''
    exec sp__script_reduce @normalize=4 -- convert to unicode
    -- insert #src(line) select ''if @@error=0 commit''
    -- insert #src(line) select ''if @@trancount>0 begin print ''''rollback of ''+@obj+'''''' rollback end''
    insert #src(line) select ''GO''
    select @j=null
    select @k=count(*) from #src
    select @j=lno/10 from #src where line like ''%ntext%syscomments%''
    if not @j is null
        begin
        insert into #src(line) select ''-- keepeye: ntext near syscomments can be an error, ''+convert(sysname,@k-@j)+'' lines above''
        insert #src(line) select ''GO''
        end
    select @j=null
    select @k=count(*) from #src
    select @j=lno/10 from #src where line like ''%substring%8000%'' or line like ''%left%8000%'' or line like ''%right%8000%''
    if not @j is null
        begin
        insert into #src(line) select ''-- keepeye: 8000 into substring, left, right can be an error, ''+convert(sysname,@k-@j)+'' lines above''
        insert #src(line) select ''GO''
        end
    select @j=null
    select @k=count(*) from #src
    select @j=lno/10 from #src where line like ''%datalength%''
    if not @j is null
        begin
        insert into #src(line) select ''-- keepeye: use of datalength can cause error, ''+convert(sysname,@k-@j)+'' lines above''
        insert #src(line) select ''GO''
        end
    -- out the code
    exec sp__script ''#src''
    end -- while
exec sp__elapsed @timer out,''-- done %d objects in:'',@v1=@n

skip_code: -- end of code convertion

exec sp__printf ''\n\n-- ### table conversion ###''

exec sp__printf ''-- drop fkeys'',@force=0
exec sp__printf ''raiserror(''''%s'''',10,1)'',''Dropping fkeys...'',@force=0

select @obj=null,@i=min(id),@n=max(id) from #objs
while (@i<=@n)
    begin
    select @obj=null
    select @obj=obj,@xtype=xtype,@parent_obj=parent_obj from #objs where id=@i and xtype=''F''
    select @i=@i+1
    if @obj is null continue -- skip non tables
    select @tmp=object_name(@parent_obj)
    select @sql=quotename(parsename(@obj,1))
    exec sp__printf ''alter table %s drop constraint %s'',@tmp,@sql
    end

select @obj=null,@i=min(id),@n=max(id) from #objs
while (@i<=@n)
    begin
    select @obj=null
    select @obj=obj,@xtype=xtype from #objs where id=@i and xtype=''U''
    select @i=@i+1
    if @obj is null continue -- skip non tables

    select @tmp=quotename(''tmp_''+parsename(@obj,1))
    select @flds=dbo.fn__flds_of(@obj,'','',null)
    set @flds=dbo.fn__flds_quotename(@flds,'','')

    exec sp__printf ''raiserror(''''Converting table %s...'''',10,1)'',@obj,@force=0
    exec sp__printf ''\n\n-- converting table:%s:'',@obj,@force=0
    exec sp__printf ''declare @rows bigint,@rows_s bigint,@msg sysname'',@force=0
    exec sp__printf ''select @rows_s=count(*) from %s'',@obj,@force=0
    exec sp__printf ''-- make a backup of data'',@force=0
    exec sp__printf ''if dbo.fn__exists(''''%s'''',''''u'''')=1 drop table %s'',@tmp,@tmp,@force=0
    exec sp__printf ''select top 0 * into %s from %s with (tablockx)'',@tmp,@obj,@force=0

    declare @has_id bit

    select @has_id=objectproperty(object_id(@obj),''TableHasIdentity'')
    if @has_id=1 exec sp__printf ''set identity_insert %s on'',@tmp,@force=0
    exec sp__printf ''insert into %s(%s) select %s from %s with (tablockx)'',@tmp,@flds,@flds,@obj,@force=0
    if @has_id=1 exec sp__printf ''set identity_insert %s off'',@tmp,@force=0
    exec sp__printf ''select @msg=convert(sysname,@rows)+'''' rows exported'''''',@force=0
    exec sp__printf ''raiserror(@msg,10,1)'',@force=0
    exec sp__printf ''select @rows=count(*) from %s'',@tmp,@force=0
    exec sp__printf ''if @rows!=@rows_s raiserror(''''there are differences with local backup'''',20,1)'',@force=0
    exec sp__printf ''drop table %s'',@obj,@force=0

    truncate table #src
    exec sp__script @obj,''#src'',@oc=@oc_tbl
    delete from #src where line=''GO''
    exec sp__script_reduce @normalize=4 -- convert to unicode
    exec sp__script ''#src''

    if @has_id=1 exec sp__printf ''set identity_insert %s on'',@obj,@force=0
    exec sp__printf ''insert into %s(%s) select %s from %s'',@obj,@flds,@flds,@tmp,@force=0
    if @has_id=1 exec sp__printf ''set identity_insert %s off'',@obj,@force=0
    exec sp__printf ''select @rows=count(*) from %s'',@tmp,@force=0
    exec sp__printf ''select @msg=convert(sysname,@rows)+'''' rows imported'''''',@force=0
    exec sp__printf ''raiserror(@msg,10,1)'',@force=0
    exec sp__printf ''if @rows!=@rows_s raiserror(''''restore of data failed'''',20,1)'',@force=0
    truncate table #src
    exec sp__script @obj,''#src'',@oc=@oc_prp
    exec sp__script ''#src''
    truncate table #src
    exec sp__script @obj,''#src'',@oc=@oc_dri
    exec sp__script @obj,''#src'',@oc=@oc_trg
    exec sp__script_reduce @normalize=4 -- convert to unicode
    exec sp__script ''#src''
    exec sp__printf ''drop table %s'',@tmp,@force=0
    end -- while

exec sp__printf ''-- reload fkeys''
select @obj=null,@i=min(id),@n=max(id) from #objs
while (@i<=@n)
    begin
    select @obj=null
    select @obj=obj,@xtype=xtype,@parent_obj=parent_obj from #objs where id=@i and xtype=''U''
    select @i=@i+1
    if @obj is null continue -- skip non tables
    if dbo.fn__exists(@obj,''FK'')=1
        begin
        exec sp__printf ''raiserror(''''Recreating fkey for %s'''',10,1)'',@obj,@force=0
        exec sp__script @obj,@oc=@oc_fk
        end
    end

exec sp__printf ''-- back to multiuser mode''
exec sp__printf ''USE [master] ''
exec sp__printf ''ALTER DATABASE [%s] SET  MULTI_USER WITH ROLLBACK IMMEDIATE'',@cur_db
exec sp__printf ''ALTER DATABASE [%s] SET  MULTI_USER '',@cur_db
exec sp__printf ''USE [%s] '',@cur_db

exec sp__elapsed @timer out,''-- end at %t table script gneration in:''

goto ret

err_noobjs:     select @msg=''#!no objects to upsize'' goto ret
err_noscode:    select @msg=''#!no code found in script_code'' goto ret
err_compcode:   select @msg=''#!compiling sp,fn,v'' goto ret
err_compdri:    select @msg=''#!compiling dri'' goto ret
err_computbl:   select @msg=''#!compiling unicoded tables'' goto ret

help:
select @msg =''Generate script to convert db to unicode''
exec sp__usage @proc,@extra=@msg
select @msg=null

ret:
if not @msg is null exec sp__printf @msg
end -- proc sp__util_tounicode'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_tounicode:

-- ============================================================== sp__util_users
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_users',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=091216
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_users') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_users') with nowait
        goto skip_sp__util_users
        end
    if @ver>091216
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_users') with nowait
        goto skip_sp__util_users
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_users') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_users'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_users]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:091216\s.zaglio: (in progress) utility for users management
    c:originally from Gregory A. Larsen
*/
create proc [dbo].[sp__util_users]
as
begin

set nocount on

-- Section 1: Create temporary table to hold databases to process

-- drop table if it already exists
if (select object_id(''tempdb..##dbnames'')) is not null
  drop table ##dbnames

-- Create table to hold databases to process
create table ##dbnames (dbname varchar(128))

-- Section 2: Determine what databases have orphan users

exec master.dbo.sp_MSforeachdb ''insert into ##dbnames select ''''?'''' from master..syslogins l right join [?]..sysusers u
on l.sid = u.sid
where l.sid is null and issqlrole <> 1 and isapprole <> 1
and not (u.name in (''''INFORMATION_SCHEMA'''',''''guest'''',''''system_function_schema'''',''''sys'''',''''dbo''''))
having count(*) > 0''



-- Section 3: Create local variables needed
declare @CNT int
declare @name char(128)
declare @sid  varbinary(85)
declare @cmd nchar(4000)
declare @c int
declare @hexnum char(100)
declare @db varchar(100)

-- Section 5: Process through each database and remove orphan users
select @cnt=count(*) from ##DBNAMES
While @CNT > 0
begin

-- get the name of the top database
  select top 1 @db=dbname from ##DBNAMES

-- delete top database
  delete from ##DBNAMES where dbname = @db

  select @db=quotename(@db)

-- Build and execute command to determine if DBO is not mapped to login
  set @cmd = ''select @cnt = count(*) from master..syslogins l right join '' +
             rtrim(@db) + ''..sysusers u on l.sid = u.sid'' +
             '' where l.sid is null and u.name = ''''DBO''''''
  exec sp_executesql @cmd,N''@cnt int out'',@cnt out

-- if DB is not mapped to login that exists map DBO to SA
  if @cnt = 1
  begin
    print ''exec '' + @db + ''..sp_changedbowner ''''SA''''''
    -- exec sp_changedbowner ''SA''
  end -- if @cnt = 1


-- drop table if it already exists
if (select object_id(''tempdb..##orphans'')) is not null
  drop table ##orphans

-- Create table to hold orphan users
create table ##orphans (orphan varchar(128))

-- Build and execute command to get list of all orphan users (Windows and SQL Server)
-- for current database being processed
   set @cmd = ''insert into ##orphans select u.name from master..syslogins l right join '' +
              rtrim(@db) + ''..sysusers u on l.sid = u.sid '' +
              ''where l.sid is null and issqlrole <> 1 and isapprole <> 1 '' +
              ''and not u.name in (''''INFORMATION_SCHEMA'''',''''guest'''', '' +
              ''''''system_function_schema'''',''''sys'''',''''dbo'''')''
   exec (@cmd)


-- Are there orphans
  select @cnt = count(*) from ##orphans

  WHILE @cnt > 0
  BEGIN

-- get top orphan
  select top 1 @name= orphan from ##orphans

-- delete top orphan
  delete from ##orphans where orphan = @name

-- Build command to drop user from database.
    set @cmd = ''exec '' + rtrim(@db) + ''..sp_revokedbaccess '''''' + rtrim(@name) + ''''''''
    print @cmd
    --exec (@cmd)


-- are there orphans left
    select @cnt = count(*) from ##orphans
  end --  WHILE @cnt > 0


-- are the still databases to process
select @cnt=count(*) from ##dbnames

end -- while @cnt > 0

-- Remove temporary tables
drop table ##dbnames, ##orphans

end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_users:

-- ================================================================ sp__util_vlf
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__util_vlf',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130227
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__util_vlf') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__util_vlf') with nowait
        goto skip_sp__util_vlf
        end
    if @ver>130227
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__util_vlf') with nowait
        goto skip_sp__util_vlf
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__util_vlf') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__util_vlf'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__util_vlf]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:virtual,log,file,transaction,grow,server,down
    v:130227\s.zaglio: mixed with sp__util_drives
    v:120906\s.zaglio: show info about virtual log files
    todo:explain fields and how to read and what to do
    c:from http://www.sqlphilosopher.com/wp/category/tsqlproblemsolving/
    t:sp__util_vlf run
*/
CREATE proc sp__util_vlf
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare
    @proc sysname, @err int, @ret int -- @ret: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if charindex(''|run|'',@opt)=0 goto help

-- ============================================================== declaration ==
-- declare -- insert before here --  @end_declare bit
create table #drive_info(
    letter nchar(1),
    total_mb bigint,
    free_mb bigint,
    label nvarchar(10),
    [% free] int,
    dbs nvarchar(4000),
    logs nvarchar(4000)
    )
exec sp__util_drives

-- =========================================================== initialization ==
-- select -- insert before here --  @end_declare=1
-- ======================================================== second params chk ==
-- ===================================================================== body ==

declare @databaselist table
(
      [database]        varchar(256),
      [executionorder]  int identity(1,1) not null
)

declare @vlfdensity     table
(
      [server]          varchar(256),
      [database]        varchar(256),
      [size_mb]         int null,
      [growth]          sysname null,
      [density]         decimal(7,2),
      [unusedvlf]       int,
      [usedvlf]         int,
      [totalvlf]        int,
      [drive]           sysname null,
      [drive % free]    int null
)

declare @loginforesult table
(
      [fileid]      int null,
      [filesize]    bigint null,
      [startoffset] bigint null,
      [fseqno]      int null,
      [status]      int null,
      [parity]      tinyint null,
      [createlsn]   numeric(25, 0) null
)

declare
    @currentdatabaseid      int,
    @maxdatabaseid          int,
    @dbname                 varchar(256),
    @density                decimal(7,2),
    @unusedvlf              int,
    @usedvlf                int,
    @totalvlf               int

insert into
      @databaselist
      (
      [database]
      )
select
      [name]
from
      [sys].[sysdatabases]

select
      @currentdatabaseid = min([executionorder]),
      @maxdatabaseid = max([executionorder])
from
      @databaselist

while @currentdatabaseid <= @maxdatabaseid
      begin

            select
                  @dbname = [database]
            from
                  @databaselist
            where
                  [executionorder] = @currentdatabaseid

            delete
                  @loginforesult
            from
                  @loginforesult

            insert into
                  @loginforesult
            exec(''dbcc loginfo(['' + @dbname + '']) with no_infomsgs'')

            select
                  @unusedvlf = count(*)
            from
                  @loginforesult
            where
                  [status] = 0

            select
                  @usedvlf = count(*)
            from
                  @loginforesult
            where
                  [status] = 2

            select
                  @totalvlf = count(*)
            from
                  @loginforesult

            select
                  @density = convert(decimal(7,2),@usedvlf) / convert(decimal(7,2),@totalvlf) * 100

            insert into
                  @vlfdensity
                  (
                  [server],
                  [database],
                  [density],
                  [unusedvlf],
                  [usedvlf],
                  [totalvlf]
                  )
            values
                  (
                  @@servername,
                  @dbname,
                  @density,
                  @unusedvlf,
                  @usedvlf,
                  @totalvlf
                  )

            set @currentdatabaseid = @currentdatabaseid + 1
      end

update a set
    size_mb=size*8/1024,
    growth=case is_percent_growth
           when 1 then cast(b.growth as sysname)+''%''
           else cast(b.growth*8/1024 as sysname)+''MB''
           end,
    [drive]=letter,
    [drive % free]=[% free]
from @vlfdensity a
join sys.master_files b
on db_name(b.database_id)=a.[database]
join #drive_info c
on b.physical_name like c.letter+'':%''

-- select * from sys.master_files
select
    [server],
    [database],
    [size_mb],
    [growth],
    [density],
    [unusedvlf],
    [usedvlf],
    [totalvlf],
    [drive],
    [drive % free]
from
      @vlfdensity
order by
      [density] desc

drop table #drive_info
goto ret

-- =================================================================== errors ==
/*
err_sample1:
exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param
goto ret
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    give status about db, disks and virtual log files
    (TODO: here are to write more explanation)
    (TODO: can be simplified using  DBCC SQLPERF (LOGSPACE)?)

Parameters

Examples

''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__util_vlf'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__util_vlf:

-- ===================================================================== sp__web
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__web',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130927.1800
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__web') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__web') with nowait
        goto skip_sp__web
        end
    if @ver>130927.1800
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__web') with nowait
        goto skip_sp__web
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__web') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__web'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__web]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,web
    k:soap,web,service,webservice,call,xml,get,pos,send
    v:130927.1800\s.zaglio: added integrated authentication and refactor of errors
    v:121022.1213\s.zaglio: removed ext.ns, now is optional and a bug
    v:121019\s.zaglio: done and tested
    r:121018\s.zaglio: smart soap call and decode
    d:121018\s.zaglio: sp__web_get
    r:121017\s.zaglio: normalized @rcq (cr,lf,tab)
    r:121016\s.zaglio: testing for bad requests
    r:121015\s.zaglio: get/set a web resource or call a webservice
    t:sp__web_test 1
*/
CREATE proc sp__web
    @uri varchar(2000) = null,
    @method varchar(2000) = null,
    @ctype nvarchar(255) = null,
    @rcq nvarchar(max) = null,
    @sa nvarchar(255) = null,
    @uid varchar(100) = null, -- Domain\UserName or UserName
    @pwd varchar(100) = null,
    @rsp nvarchar(max) = null out,
    @sts nvarchar(4000) = null out,
    @opt nvarchar(255) = null,
    @dbg int=0
as
begin try
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

/*
    a mixed evolution of:
    from http://www.vishalseth.com/post/2009/12/22/
         Call-a-webservice-from-TSQL-%28Stored-Procedure%29-using-MSXML.aspx
    and  https://sourceforge.net/projects/sqldom/
*/

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=case when @opt is null then ''||'' else dbo.fn__str_quote(@opt,''|'') end

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    @i int,@n int,                          -- index, counter
    -- @sql nvarchar(max),                  -- dynamic sql
    -- options
    @nodecode bit,@oxml bit,@soap bit,
    @ia bit,                                -- integrated autentication
    @to_resolve int, @to_connect int,       -- timeouts
    @to_send int, @to_receive int,
    @crlf varchar(2),@cr char(1),@lf char(1),@tab char(1),
    @oid int,@hr int,@cmd varchar(255),
    @len int,@hs_id int,
    @sa_ns nvarchar(1024),
    @soap_exception sysname,
    @params nvarchar(max),
    @rcqprms_id int,                        -- if params given
    @rcqhdrs_id int,                        -- if more headers given
    @hre char(2),                           -- common API error
    @end_declare bit

declare @blob table(blob nvarchar(max))
declare @utf table(utf sysname)

-- =========================================================== initialization ==
select
    @hre=''hr'',
    @sts=null,@rsp=null,            -- empy for sureness
    @soap_exception=''<faultstring>System.Web.Services.Protocols.SoapException:'',
    @nodecode=charindex(''|nodecode|'',@opt),
    @oxml=charindex(''|oxml|'',@opt),
    @ia=charindex(''|ia|'',@opt),
    @crlf=crlf,@cr=cr,@lf=@lf,@tab=tab,
    @rcqprms_id=isnull(object_id(''tempdb..#rcqprms''),0),
    @rcqhdrs_id=isnull(object_id(''tempdb..#rcqhdrs''),0),
    @end_declare=1
from fn__sym()

insert @utf select N''<?xml version="1.0" encoding="utf-8"?>''
insert @utf select N''<?xml version="1.0" encoding="utf-16"?>''
-- ======================================================== second params chk ==
if isnull(@uri,'''')='''' goto help

-- ===================================================================== body ==

-- timeouts
if charindex(''|to:'',@opt)>0
    begin
    -- sp__web ''test'',@opt=''to:1000,1000,1000'',@dbg=1
    select
        @to_resolve=case pos when 1 then token else @to_resolve end,
        @to_connect=case pos when 2 then token else @to_connect end,
        @to_send   =case pos when 3 then token else @to_send    end,
        @to_receive=case pos when 4 then token else @to_receive end
    from dbo.fn__str_table(dbo.fn__str_between(@opt,''to:'',''|'',default),'','')
    if @dbg=1 exec sp__printf ''-- to:%d,%d,%d,%d'',
                              @to_resolve,@to_connect,@to_send,@to_receive
    if not coalesce(@to_resolve,@to_connect,@to_send,@to_receive) is null
    and (@to_resolve is null or @to_connect is null or
         @to_send is null or @to_receive is null)
        raiserror(''all timeouts must be specified'',16,1)
    end -- timeouts

if isnull(@sa,'''')=''''
    select
        @ctype=isnull(@ctype,''text/http''),
        @method=isnull(@method,''GET'')
else
    select
        @ctype=isnull(@ctype,''text/xml;charset=UTF-8''),
        @method=isnull(@method,''POST'')


-- create ole
select @cmd=''MSXML2.ServerXMLHTTP''
exec @hr = sp_oacreate @cmd, @oid out
IF @hr!=0 raiserror(@hre,16,1)

/*  note:
    exec @hr=sp_oacreate ''winhttp.winhttprequest.5.1'',@@oid out
    look work well too but do not remembere what limit has
    need a search on internet
*/

-- set timeouts
if not @to_resolve is null
    begin
    select @cmd=''setTimeouts''
    exec @hr = sp_OAMethod @oid, @cmd, null,
                           @to_resolve,@to_connect,@to_send,@to_receive
    IF @hr!=0 raiserror(@hre,16,1)
    end

-- open the destination URI with Specified method
select @cmd=''open''
if @ia=0
    exec @hr = sp_OAMethod @oid, @cmd, null, @method, @uri, ''false'', @uid, @pwd
else
    exec @hr = sp_OAMethod @oid, @cmd, null, @method, @uri, ''false''
IF @hr!=0 raiserror(@hre,16,1)

-- set request headers
select @cmd=''setRequestHeader''
exec @hr = sp_OAMethod @oid, @cmd, null, ''Content-Type'', @ctype
IF @hr!=0 raiserror(@hre,16,1)

-- send the request
if @rcqprms_id!=0
    begin
    if not @rcq is null
        raiserror(''#rcqprms and @rcq cannot be used together'',16,1)
    if isnull(@sa,'''')='''' raiserror(''missing SOAP Action'',16,1)
    select @rcq=''<?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema">''
    +case @ia when 0 then '''' else ''
    <soap:Header>
    <Auth user="%uid%" pass="%pwd%" xmlns="%sa_ns%" />
    </soap:Header>''
    end+''
      <soap:Body>
        <%sa% xmlns="%sa_ns%">
          %params%
        </%sa%>
      </soap:Body>
    </soap:Envelope>''

    select @sa_ns=left(@uri,patindex(''%[a-z0-9_\-\\]/[a-z0-9_\-\\]%'',@uri))
    select @i=dbo.fn__charindex(''/'',@sa,-1)
    if @i>1 select @sa_ns=left(@sa,@i-1),@sa=substring(@sa,@i+1,len(@sa))

    select @params=isnull(@params+@crlf,'''')+''<''+var+''>''+val+''</''+var+''>''
    from #rcqprms

    exec sp__str_replace @rcq out,''%sa%|%sa_ns%|%uid%|%pwd%'',
                                  @sa,@sa_ns,@uid,@pwd
    select @rcq=replace(@rcq,''%params%'',@params)

    select @sa=@sa_ns+''/''+@sa

    end -- use of #rcqprms

-- can be used fn__sql_trim but is slower in a repetitive call
while left(@rcq,2)=@crlf select @rcq=substring(@rcq,3,len(@rcq))
while left(@rcq,1)=@cr select @rcq=substring(@rcq,2,len(@rcq))
while left(@rcq,1)=@lf select @rcq=substring(@rcq,2,len(@rcq))
select @rcq=ltrim(rtrim(replace(@rcq,@tab,''    '')))

if nullif(@rcq,'''') is null raiserror(''undefined request'',16,1)

if @dbg=1
    begin
    exec sp__printf ''-- @uri=%s, @ctype=%s, @method=%s, @sa=%s\n-- request:'',
                    @uri,@ctype,@method,@sa

    exec sp__printsql @rcq
    end

if isnull(@sa,'''')!='''' select @soap=1 else select @soap=0

-- set soap action
if @soap=1
    begin
    select @cmd=''SOAPAction''
    exec @hr = sp_oamethod @oid, ''setRequestHeader'', null, @cmd, @sa
    if @hr!=0 raiserror(@hre,16,1)
    set @len = len(@rcq)
    select @cmd=''content-Length''
    exec @hr = sp_oamethod @oid, ''setRequestHeader'', null, @cmd, @len
    if @hr!=0 raiserror(@hre,16,1)
    end

-- more headers
if @rcqhdrs_id!=0
    begin
    declare @key nvarchar(500), @val nvarchar(500)
    declare cs cursor local for
    select [key],[val]
    from #rcqhdrs
    where 1=1
        open cs
        while 1=1
            begin
            fetch next from cs into @key,@val
            if @@fetch_status!=0 break

            select @cmd=''setRequestHeader''
            exec @hr = sp_oamethod @oid, @cmd, null, @key, @val
            if @hr!=0 break

            end -- cursor cs
        close cs
        deallocate cs
        if @hr!=0 raiserror(@hre,16,1)
    end -- more headers

select @cmd=''send''
exec @hr = sp_oamethod @oid, @cmd, null, @rcq
if @hr!=0 raiserror(@hre,16,1)

-- Get status text
select @cmd=''StatusText''
exec sp_OAGetProperty @oid, @cmd, @sts out
if @hr!=0 raiserror(@hre,16,1)
select @cmd=''Status''
exec sp_OAGetProperty @oid, @cmd, @hs_id out
if @hr!=0 raiserror(@hre,16,1)
select @sts=cast(@hs_id as sysname) + ''|''+ @sts

-- Get response text
select @cmd=''responseText''
insert into @blob(blob)
exec @hr = sp_OAGetProperty @oid, @cmd -- , @rsp out
/*  sp_OAGetProperty or any extended sp, do not support varchar(max)
    however returning as a resulset will return long results        */

select top 1 @rsp=blob from @blob

if @nodecode=0 select @rsp=dbo.fn__web_html_decode(@rsp)
if @soap=1 and @oxml=0
    begin
    -- remove <?xml tags
    update @utf set @rsp=replace(@rsp,utf,'''')
    select @i=charindex(N''<soap:Body>'',@rsp)
    select @i=charindex(N''<'',@rsp,@i+1)
    select @n=charindex(N''</soap:Body>'',@rsp,@i)
    select @rsp=substring(@rsp,@i,@n-@i)

    -- remove most external namespaces
    select @i=charindex(N''xmlns="'',@rsp)
    if @i>0
        begin
        select @n=charindex(N''"'',@rsp,@i+7)
        if @n<@i or @n=0 raiserror(''inside error stripping namespace'',16,1)
        select @rsp=left(@rsp,@i-1)+substring(@rsp,@n+1,len(@rsp))
        end
    end -- xmp strip

if @dbg=1
    begin
    exec sp__printf ''-- response:''
    exec sp__printsql @rsp
    end

if not @hs_id in (0,200)
    begin
    if charindex(@soap_exception,@rsp)>0
        raiserror(''soap exception'',16,1)
    else
        raiserror(''status ko'',16,1)
    end

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

if not @oid is null exec sp_OADestroy @oid

goto ret

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    get an html page or call a webservice

Notes
    * see "sp__util_advopt" on how disable protection
    * remember to put N before strings to correctly send unicode chars
    * ensure that exists in your web.config the parameter:
        <system.web>
            <globalization requestEncoding="UTF-8" responseEncoding ="UTF-8"/>
    * soap exception and status error raise a error stored into @sts or @rsp
      (@sts is "500|Internal Server Error" and @rsp contain the
      System.Web.Services.Protocols.SoapException message)
    * SOAP ACTION and SOAP PARAMS are CASE SENSITIVE
    * because the namespace is self-determined by the @sa,
      some problems can arise where there are more than one namespace

Parameters
    @uri        web address
    @method     optional method (default is GET)
    @ctype      content type (default TEXT/HTTP)
    @rcq        data to send or request body
    @sa         optional SOAP action
    @uid        optional Domain\UserName or UserName
    @pwd        optional password
    @rsp        (out)response
    @sts        (out)HTTP Status as id|txt (200=ok, 404=not found, etc.)
    #rcqhdrs    optional multiple request headers
                create table #rcqhdrs([key] nvarchar(500),val nvarchar(500))
    #rcqprms    optional to auto fill xml structure to call del SOAP ws
                create table #rcqprms(
                    id int identity, rid int default(0),
                    var nvarchar(500),
                    val nvarchar(max)
                    )
    @opt        option      description
                ----------- ----------------------------------------------------
                nodecode    disable html decode for faster execution
                to:r,c,r,s  set timeouts(ms) for receive,connect,send,receive
                oxml        do not strip SOAP headers but return original xml
                wslist      (TODO) instead of get htm page, get the list of srvs
                wsdl        (TODO) instead of call the SOAP method, get the DL
                ia          @uid & @pwd are passed as integrated authentication

Examples
    see sp__web_ws_test
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret
end try

-- =================================================================== errors ==
begin catch
select @e_msg=error_message()
if @e_msg=@hre
    exec sp_oageterrorinfo @oid,@e_p2 out,@e_p3 out

-- destroy ole object
if not @oid is null exec sp_OADestroy @oid
select @oid=null

if @e_msg=@hre
    begin
    select @e_msg=''cmd ole "%s" (src:%s; msg:%s)'',@e_p1=@cmd
    exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,@opt=''ex''
    end
else
    exec @ret=sp__err @cod=@proc,@opt=''ex''
return @ret
end catch -- proc sp__web'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__web:

-- ============================================================== sp__web_submit
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__web_submit',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120905
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__web_submit') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__web_submit') with nowait
        goto skip_sp__web_submit
        end
    if @ver>120905
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__web_submit') with nowait
        goto skip_sp__web_submit
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__web_submit') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__web_submit'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__web_submit]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:form, post, submit, simulate, simulation
    v:120905\s.zaglio: removed dbg info
    v:120830.1000\s.zaglio: submit html form
*/
CREATE proc sp__web_submit
    @url varchar(4000) = null,
    @params varchar(4000) = null,
    @ResponseText nvarchar(4000) = null out,    -- NO vcMAX!
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare
    @proc sysname, @err int, @ret int -- @ret: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
select
    @url=isnull(@url,''''),
    @params=isnull(@params,'''')

if (@url='''' or @params ='''') -- and @opt=''||''
    goto help

-- ============================================================== declaration ==
declare
    @obj int,
    @hr int,
    @src varchar(1000),
    @desc varchar(1000),
    @cmd varchar(128)

-- =========================================================== initialization ==
-- select -- insert before here --  @end_declare=1
-- ======================================================== second params chk ==
-- ===================================================================== body ==

if @dbg=1 exec sp__printf ''-- url:%s\n-- p:%s'',@url,@params

select @cmd=''Msxml2.ServerXMLHTTP.3.0''
exec @hr = sp_oaCreate @cmd, @obj OUTPUT
if @hr!=0 goto err_ole

select @cmd=''Open''
exec @hr = sp_OAMethod @obj, @cmd, null, ''POST'',@url, 0
if @hr!=0 goto err_ole

select @cmd=''setRequestHeader''
exec @hr = sp_OAMethod @obj, @cmd, null,
                             ''Content-Type'',
                             ''application/x-www-form-urlencoded''
if @hr!=0 goto err_ole

select @cmd=''send''
exec @hr = sp_OAMethod @obj, @cmd, null, @params
if @hr!=0 goto err_ole

select @cmd=''responseText''
exec @hr = sp_OAGetProperty @obj, @cmd, @ResponseText OUT
if @hr!=0 goto err_ole

-- Destroy the object
dispose:
exec @hr = sp_OADestroy @obj
select @obj=null

goto ret

-- =================================================================== errors ==
err_ole:
exec sp_OAGetErrorInfo @obj, @src OUT, @desc OUT
exec @ret=sp__err ''"%s" in "%s" for OLE cmd %s'',@proc,
                  @p1=@desc,@p2=@src,@p3=@cmd
goto dispose

-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    submit html form

Parameters
    @url            target address (tipical ACTION tag)
    @params         form parameters (tipical INPUT tag):
                    param1=val1&param2=val2&param3=...
    @ResponseText   the returned html/text
    @opt            options (not used)
    @dbg            debug
                    1 print some info

Examples
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__web_submit'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__web_submit:

-- ================================================================ sp__web_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__web_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121019
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__web_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__web_test') with nowait
        goto skip_sp__web_test
        end
    if @ver>121019
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__web_test') with nowait
        goto skip_sp__web_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__web_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__web_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__web_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,web,test
    v:121019\s.zaglio: done test 4 and xml extraction
    r:121018\s.zaglio: testing smart soap call
    r:121017\s.zaglio: adapted sp__web_ws to null prev bugs
    r:121016\s.zaglio: bug near N'' and crlf
    r:121015\s.zaglio: test sp__web_ws
    t:sp__web_test ''all'',@dbg=1
*/
CREATE proc sp__web_test
    @tst sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @opt1 bit,@opt2 bit,
    @crlf nvarchar(2),
    @response nvarchar(max),
    @status nvarchar(4000),
    @url nvarchar(1024),
    @sa  nvarchar(1024),
    @xml xml,
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @opt1=charindex(''|opt|'',@opt),
    @crlf=crlf,
    @tst=isnull(@tst,''''),
    @end_declare=1
from fn__sym()

-- ======================================================== second params chk ==
if @tst='''' goto help
if @tst=''all'' select @tst=''0''
if isnumeric(@tst)=0 goto err_tst
if @tst<0 or @tst>4 goto err_tst

-- ===================================================================== body ==
-- sp__web_test run,@dbg=1
if @tst in (0,1)
    begin
    exec sp__prints ''simple url get''
    select @url=''http://www.webservicex.com/stockquote.asmx/GetQuote?symbol=MSFT''
    exec @ret=sp__web @url,@rsp=@response out,@sts=@status out,@dbg=@dbg
    if @ret in (0,-176288843) exec sp__printsql @response
    else exec sp__printf ''ret=%d, status=%s'',@ret,@status
    end

-- sp__web_test 2,@dbg=1
if @tst in (0,2)
    begin
    exec sp__prints ''not exists test''
    select @url=''http://www.thisdonotexist.exw/i.html''
    exec @ret=sp__web @url,@dbg=@dbg
    if @ret in (0,-176288843) exec sp__printsql @response
    else exec sp__printf ''ret=%d, status=%s'',@ret,@status

    exec sp__prints ''bad method''
    select @url=''http://www.webservicex.com/stockquote.asmx/GetQuote?symbol=MSFT''
    exec @ret=sp__web @url,''got'',@dbg=@dbg
    if @ret in (0,-176288843) exec sp__printsql @response
    else exec sp__printf ''ret=%d, status=%s'',@ret,@status
    end

-- sp__web_test 3,@dbg=1
if @tst in (0,3)
    begin
    exec sp__prints ''web service test with timeouts and xml parse''
    select @url=''http://www.webservicex.net/globalweather.asmx''
    exec @ret=sp__web @url,
        @rsp=@response out,
        @sa=''http://www.webserviceX.NET/GetWeather'',
        @rcq=''
        <?xml version="1.0" encoding="utf-8"?>
        <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <soap:Body>
            <GetWeather xmlns="http://www.webserviceX.NET">
              <CityName>brescia</CityName>
              <CountryName>italy</CountryName>
            </GetWeather>
          </soap:Body>
        </soap:Envelope>''
        ,@opt=''to:1000,1000,1000,1000''
        ,@sts=@status out,@dbg=@dbg --,@opt=''nodecode''
    if @ret in (0,-176288843) exec sp__printsql @response
    else exec sp__printf ''ret=%d, status=%s'',@ret,@status

    -- extract xml data
    select @xml = cast(@response as xml)
    select
        t.c.value(''Location[1]'', ''nvarchar(20)'') as ''Location'',
        t.c.value(''Time[1]'',     ''nvarchar(10)'') as ''Time'',
        t.c.value(''Wind[1]'',     ''nvarchar(50)'') as ''Wind'',
        t.c.value(''Visibility[1]'', ''nvarchar(200)'') as ''Visibility'',
        -- alternative direct method
        @xml.value(''(//CurrentWeather/SkyConditions)[1]'', ''nvarchar(20)'') SkyCond,
        @xml.value(''(//CurrentWeather/temperature)[1]'', ''nvarchar(20)'') Temp,   -- case sensitive
        @xml.value(''(//CurrentWeather/DewPoint)[1]'', ''nvarchar(20)'') DewPoint,
        @xml.value(''(//CurrentWeather/RelativeHumidity)[1]'', ''nvarchar(20)'') Humidity,
        @xml.value(''(//CurrentWeather/Pressure)[1]'', ''nvarchar(20)'') Pressure,
        @xml.value(''(//CurrentWeather/Status)[1]'', ''nvarchar(20)'') Status
    from @xml.nodes(''//CurrentWeather'') t(c)
    end -- test 3

-- sp__web_test 4,@dbg=1
if @tst in (0,4)
    begin
    exec sp__prints ''smart SOAP call''
    create table #rcqprms(
        id int identity, rid int default(0),
        var nvarchar(500),
        val nvarchar(max)
        )

    insert #rcqprms(var,val) select ''CityName'',''brescia''
    insert #rcqprms(var,val) select ''CountryName'',''italy''

    select @url=''http://www.webserviceX.NET/globalweather.asmx'',
           @sa =''GetWeather''
    exec @ret=sp__web @uri=@url,
                      @sa=@sa,@sts=@status out,@rsp=@response out
                      ,@dbg=@dbg
    if @ret in (0,-176288843) exec sp__printsql @response
    else exec sp__printf ''ret=%d, status=%s'',@ret,@status

    drop table #rcqprms
    end -- soap smart call

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_tst:    select @e_msg=''wrong @tst value''                            goto err
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test the sp__web_ws:
    1. simple test (get a url)
    2. wrong test (bad url)
    3. wrong method (got instead of get)

Parameters
    @tst    id of specific test
            1   test simple get
            2   test wrong params
            3   test SOAP call
            4   test SOAP smart call
            ALL all above
    @opt    options (not used)
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__web_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__web_test:

-- ================================================================ sp__web_tool
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__web_tool',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=120905
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__web_tool') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__web_tool') with nowait
        goto skip_sp__web_tool
        end
    if @ver>120905
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__web_tool') with nowait
        goto skip_sp__web_tool
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__web_tool') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__web_tool'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__web_tool]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    k:html,parse,get,table
    v:120905\s.zaglio: quiet out, use dbg to show info
    v:120824.1400\s.zaglio: get a table but not all
    r:120823\s.zaglio: add temp SP for web operations
    t:sp__web_tool @opt=''install''
*/
CREATE proc sp__web_tool
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp
declare
    @proc sysname, @err int, @ret int -- @ret: 0=OK -1=HELP, any=error id
select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')
-- ========================================================= param formal chk ==
if @opt=''||'' goto help

-- ============================================================== declaration ==
-- declare -- insert before here --  @end_declare bit
-- =========================================================== initialization ==
-- select -- insert before here --  @end_declare=1
-- ======================================================== second params chk ==
-- ===================================================================== body ==

-- ##########################
-- ##
-- ## BEGIN OF RUETER''S CODE
-- ##
-- ########################################################

/*
SQLDOM HTML parser and DOM tools for MSSQL.
https://sourceforge.net/projects/sqldom/

Parses HTML from a string or from a URL into a DOM (document object model)
implemented with SQL tables.  Provides routines to manipulate the DOM data
and to render the DOM data back to HTML.

You may safely run this entire script:  it does not make any changes to any
SQL user databases.  It only creates some local temporary tables and temporary
stored procedures, and prints out a string with some instructions.

Requires Microsoft SQL 2005 or later.

Copyright (C) 2012 David B. Rueter (drueter@assyst.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

HISTORY

Version .924 4/24/2012
  Fixed attribute handling to support attributes without values (such as <
  option selected>My Option</option>)  Thanks to JMelin for reporting.

Version .923 4/3/2012
  Fixed additional bug in #spgetDOM pertaining to getting by selector.

Version .922 3/20/2012
  Fixed bug in #spgetDOM pertaining to getting by selector.  Thanks to Brian Hurtt
  for reporting and providing correction.

Version .921 3/5/2012
  Added #sputilConvertJSONToXML to convert JSON data to XML

Version .920 2/23/2012
  Refactor #spgetDOMHTML to fix bugs, streamline

Version .919 2/21/2012
  Corrected problem with rendering HTML comments

Version .918  2/20/2012
  Removed dependencies on 3 UDF string helper functions
  Performance increase (approx. 23%)
  Clean up some comments

Version .917  2/19/2012
  Initial public version

*/

SET NOCOUNT ON

IF OBJECT_ID(''tempdb..#spactTrimWhitespace'') IS NOT NULL BEGIN
  DROP PROCEDURE #spactTrimWhitespace
END

IF OBJECT_ID(''tempdb..#spgetLenNTW'') IS NOT NULL BEGIN
  DROP PROCEDURE #spgetLenNTW
END

if charindex(''|keep|'',@opt)=0
    begin

    IF OBJECT_ID(''tempdb..#tblDOMDocs'') IS NOT NULL BEGIN
      DROP TABLE #tblDOMDocs
    END

    IF OBJECT_ID(''tempdb..#tblDOM'') IS NOT NULL BEGIN
      DROP TABLE #tblDOM
    END

    IF OBJECT_ID(''tempdb..#tblDOMAttribs'') IS NOT NULL BEGIN
      DROP TABLE #tblDOMAttribs
    END

    IF OBJECT_ID(''tempdb..#tblDOMStyles'') IS NOT NULL BEGIN
      DROP TABLE #tblDOMStyles
    END
    end -- keep option

IF OBJECT_ID(''tempdb..#spactDOMOpen'') IS NOT NULL BEGIN
  DROP PROCEDURE #spactDOMOpen
END

IF OBJECT_ID(''tempdb..#spgetDOM'') IS NOT NULL BEGIN
  DROP PROCEDURE #spgetDOM
END

IF OBJECT_ID(''tempdb..#spgetDOMHTML'') IS NOT NULL BEGIN
  DROP PROCEDURE #spgetDOMHTML
END

IF OBJECT_ID(''tempdb..#spactDOMLoad'') IS NOT NULL BEGIN
  DROP PROCEDURE #spactDOMLoad
END

IF OBJECT_ID(''tempdb..#spinsDOMNode'') IS NOT NULL BEGIN
  DROP PROCEDURE #spinsDOMNode
END

IF OBJECT_ID(''tempdb..#spactDOMClear'') IS NOT NULL BEGIN
  DROP PROCEDURE #spactDOMClear
END

IF OBJECT_ID(''tempdb..#spupdDOMAttribs'') IS NOT NULL BEGIN
  DROP PROCEDURE #spupdDOMAttribs
END

IF OBJECT_ID(''tempdb..#spupdDOMStyles'') IS NOT NULL BEGIN
  DROP PROCEDURE #spupdDOMStyles
END

IF OBJECT_ID(''tempdb..#sputilGetHTTP'') IS NOT NULL BEGIN
  DROP PROCEDURE #sputilGetHTTP
END

IF OBJECT_ID(''tempdb..#sputilConvertJSONToXML'') IS NOT NULL BEGIN
  DROP PROCEDURE #sputilConvertJSONToXML
END

if charindex(''|uninstall|'',@opt)>0 goto ret
if charindex(''|install|'',@opt)>0
begin
/*
**************************************************************************************
PROCEDURE #spactTrimWhitespace
Simple helper function to do a left-trim  or right-trim of whitespace (spaces, tabs,
carriage returns and linefeeds, and tabs).
I would really prefer this to be a function, but we are not allowed to create
temporary functions, and I do not want SQLDOM to require permanent database objects.
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spactTrimWhitespace
@S varchar(MAX) OUTPUT,
@DoLeft bit = 0,
@DoRight bit = 1
AS BEGIN
  DECLARE @P int

  IF @DoRight = 1 BEGIN
    --Right trim
    SET @P = LEN(@S + ''''x'''') - 1
    WHILE @P >= 1 BEGIN
      IF ISNULL(SUBSTRING(@S, @P, 1), '''' '''') IN ('''' '''', CHAR(9), CHAR(10), CHAR(13)) BEGIN
        SET @P = @P - 1
      END
      ELSE BEGIN
        BREAK
      END
    END

    SET @S= LEFT(@S, @P)
  END

  IF @DoLeft = 1 BEGIN
    --Left trim
    SET @P = 1
    WHILE @P <= LEN(@S + ''''x'''') - 1 BEGIN
      IF SUBSTRING(@S, @P, 1) IN  ('''' '''', CHAR(9), CHAR(10), CHAR(13)) BEGIN
        SET @P = @P + 1
      END
      ELSE BEGIN
        BREAK
      END
    END

    SET @S = RIGHT(@S, LEN(@S + ''''x'''') - 1 - @P + 1)
  END

END
'')

/*
**************************************************************************************
PROCEDURE #spgetLenNTW (no trailing whitespace)
Simple helper function to determine the length of a string after trimming all
trailing whitespace (spaces, tabs, carriage returns and linefeeds, and tabs).
I would really prefer this to be a function, but we are not allowed to create
temporary functions, and I do not want SQLDOM to require permanent database objects.
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spgetLenNTW
@S varchar(MAX),
@Len int OUTPUT
AS
BEGIN
  SET @Len = LEN(@S + ''''x'''') - 1

  DECLARE @Done bit
  SET @Done = 0

  WHILE @Done = 0 BEGIN
    IF (@Len > 0) AND (SUBSTRING(@S, @Len, 1) IN (CHAR(9), CHAR(10), CHAR(13), '''' '''')) BEGIN
      SET @Len = @Len - 1
    END
    ELSE BEGIN
      SET @Done = 1
    END
  END

END
'')

/*
**************************************************************************************
PROCEDURE #spactDOMOpen
Procedure #spactDOMOpen verifies session and @DocID
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spactDOMOpen
@DocID int OUTPUT,
@CreateNew bit = 0
AS
BEGIN
  --Note:  if @DocID is provided, we trust it.  We don''''t validate that it exists
  --or that it belongs to this session.

  IF (@CreateNew = 1) BEGIN
    IF @DocID IS NOT NULL BEGIN
      RAISERROR(''''Error in #spactDOMOpen:  Cannot specify @DocID if @CreateNew=1'''', 16, 1)
    END

    INSERT INTO #tblDOMDocs (DateCreated)
    VALUES (GETDATE())

    SET @DocID = SCOPE_IDENTITY()
  END
  ELSE BEGIN
    IF @DocID IS NOT NULL BEGIN
      IF NOT EXISTS (SELECT DocID FROM #tblDomDocs WHERE DocID = @DocID) BEGIN
        RAISERROR(''''Error in #spactDOMOpen: Invalid @DocID specified.'''', 16, 1)
      END
    END
    ELSE BEGIN
      --Open a new DOM Document

      DECLARE @DocCount int
      IF @DocID IS NULL BEGIN
        SELECT
          @DocCount = COUNT(doc.DocID),
          @DocID = MIN(doc.DocID)
        FROM
          #tblDOMDocs doc

        IF @DocCount > 1 BEGIN
          RAISERROR(''''Error in #spactDOMOpen:  @DocID was not specified, and there are multiple documents present in this session.'''', 16, 1)
        END
        ELSE IF @DocID IS NULL BEGIN
          INSERT INTO #tblDOMDocs (DateCreated)
          VALUES (GETDATE())

          SET @DocID = SCOPE_IDENTITY()
        END
      END
    END
  END
END
'')

/*
**************************************************************************************
PROCEDURE #spactDOMClear
Procedure #spactDOMClear clears all data in the DOM
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spactDOMClear
@DocID int = NULL OUTPUT
AS BEGIN

  DELETE FROM #tblDOMAttribs WHERE DEID IN (SELECT DEID FROM #tblDOM WHERE @DocID IS NULL OR DocID = @DocID)
  DELETE FROM #tblDOMStyles WHERE DEID IN (SELECT DEID FROM #tblDOM WHERE @DocID IS NULL OR DocID = @DocID)
  DELETE FROM #tblDOM WHERE @DocID IS NULL OR DocID = @DocID

END
'')

/*
**************************************************************************************
PROCEUDRE #spupdDOMAttribs
Procedure #spupdDOMAttribs is to set Attributes of existing elements in the DOM
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spupdDOMAttribs
@DocID int = NULL OUTPUT,
@DEID int = NULL,
@ID varchar(512) = NULL,
@Name varchar(512) = NULL,
@Value varchar(MAX) = NULL,
@Attribs varchar(MAX) = NULL,
@Selector varchar(MAX) = NULL
AS
BEGIN
  SET @Value = NULLIF(RTRIM(@Value), '''''''')

  IF @DocID IS NULL EXEC #spactDOMOpen @DocID = @DocID OUTPUT

  DECLARE @tvTargetList TABLE (
    DEID int PRIMARY KEY
  )

  IF @ID IS NOT NULL BEGIN
    SELECT @DEID = dom.DEID
    FROM
      #tblDOM dom
    WHERE
      dom.DocID = @DocID AND
      dom.ID = @ID
  END
  ELSE IF @Selector IS NOT NULL BEGIN
    INSERT INTO @tvTargetList (DEID)
    EXEC #spgetDOM @DocID = @DocID OUTPUT, @Selector = @Selector, @ReturnDEIDsOnly = 1
    SELECT TOP 1 @DEID = DEID FROM @tvTargetList
  END

  WHILE @DEID IS NOT NULL BEGIN

    IF ISNULL(RTRIM(@Attribs), '''''''') = '''''''' BEGIN
      DECLARE @TargetID int
      SELECT @TargetID = atr.DomAttribID
      FROM #tblDOMAttribs atr
      WHERE
         atr.DEID = @DEID AND
         atr.Name = @Name


      IF @TargetID IS NOT NULL BEGIN
        IF @Value IS NULL BEGIN
          DELETE FROM #tblDOMAttribs WHERE DOMAttribID = @TargetID
        END
        ELSE BEGIN
          UPDATE #tblDOMAttribs SET Value = @Value WHERE DOMAttribID = @TargetID
        END
      END
      ELSE BEGIN
        INSERT INTO #tblDOMAttribs (
          DEID,
          Name,
          Value)
        VALUES (
          @DEID,
          @Name,
          @Value
        )
      END

      --Assign special attributes
      UPDATE dom
      SET
        ID = ISNULL(at_id.Value, dom.ID),
        Name = ISNULL(at_name.Value, dom.Name),
        Class = ISNULL(at_class.Value, dom.Class)
      FROM
        #tblDOM dom
        LEFT JOIN #tblDOMAttribs at_id ON dom.DEID = at_id.DEID AND at_id.Name = ''''id''''
        LEFT JOIN #tblDOMAttribs at_name ON dom.DEID = at_name.DEID AND at_name.Name = ''''name''''
        LEFT JOIN #tblDOMAttribs at_class ON dom.DEID = at_class.DEID AND at_class.Name = ''''class''''
      WHERE
        dom.DocID = @DocID AND
        dom.DEID = @DEID


      DELETE FROM #tblDOMAttribs
      WHERE
        DEID = @DEID AND
        Name in (''''id'''', ''''name'''', ''''class'''')
    END
    ELSE BEGIN
      IF RTRIM(ISNULL(@Attribs, '''''''')) <> '''''''' BEGIN
        --Parse out attributes
        DECLARE @i int
        DECLARE @c char
        DECLARE @State varchar(40)
        DECLARE @InQuote bit
        DECLARE @NameStr varchar(MAX)
        DECLARE @ValueStr varchar(MAX)
        DECLARE @StartQuote char

        DECLARE @DoAttrib bit

        SET @InQuote = 0

        SET @StartQuote = NULL
        SET @State = ''''AttribName''''
        SET @i = 1

        SET @NameStr = ''''''''
        SET @ValueStr = ''''''''

        WHILE @i <= LEN(@Attribs) BEGIN
          SET @c = SUBSTRING(@Attribs, @i, 1)

          IF (@State = ''''AttribValue'''') BEGIN
            IF (@c IN (''''"'''', '''''''''''''''')) BEGIN
              IF (@InQuote = 0) AND ((@StartQuote IS NULL) OR (@c = @StartQuote)) BEGIN
                SET @InQuote = 1
                IF @StartQuote IS NULL BEGIN
                  SET @StartQuote = @c
                END
              END
              ELSE IF (@InQuote = 1) AND (@c = @StartQuote) BEGIN
                SET @InQuote = 0
                IF (@i >= 2) AND (SUBSTRING(@Attribs, @i -1 , 1) = @c) BEGIN
                  SET @ValueStr = @ValueStr + @C
                END
              END
              ELSE IF @c <> @StartQuote BEGIN
                SET @ValueStr = @ValueStr + @c
              END
            END
            ELSE BEGIN
              SET @ValueStr = @ValueStr + @c
            END

            IF ((@c = '''' '''') AND @InQuote = 0) OR (@i = LEN(@Attribs)) BEGIN
              SET @DoAttrib = 1
              SET @State = ''''AttribName''''
            END
          END

          ELSE BEGIN
            IF @State = ''''AttribName'''' BEGIN
              IF @c = ''''='''' BEGIN
                SET @State = ''''AttribValue''''
              END
              ELSE IF (@c = '''' '''') BEGIN
                SET @DoAttrib = 1
              END
              ELSE IF @i = LEN(@Attribs) BEGIN
                SET @NameStr = @NameStr + @c
                SET @DoAttrib = 1
              END
              ELSE BEGIN
                IF @c <> '''' '''' BEGIN
                  SET @NameStr = @NameStr + @c
                END
              END
            END
          END

          IF @DoAttrib = 1 BEGIN
            SET @DoAttrib = 0
            EXEC #spupdDOMAttribs
              @DocID = @DocID OUTPUT,
              @DEID = @DEID,
              @Name = @NameStr,
              @Value = @ValueStr

            SET @NameStr = ''''''''
            SET @ValueStr = ''''''''
          END

          SET @i = @i + 1
        END

      END
    END

    DELETE FROM @tvTargetList WHERE DEID = @DEID

    SET @DEID = NULL

    IF EXISTS(SELECT DEID FROM @tvTargetList) BEGIN
      SELECT TOP 1 @DEID = DEID FROM @tvTargetList
    END
  END
END
'')

/*
**************************************************************************************
PROCEDURE #spinsDOMNode
Procedure #spinsDOMNode is to ADD elements to the DOM
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spinsDOMNode
@DocID int = NULL OUTPUT,
@Tag varchar(MAX),
@ID varchar(512) = NULL,
@Name varchar(512) = NULL,
@Class varchar(512) = NULL,
@Text varchar(MAX) = NULL,
@Attribs varchar(MAX) = NULL,
@OpenTagStartPos int = NULL,
@CloseTagEndPos int = NULL,
@ParentID varchar(512) = NULL,
@ParentDEID int = NULL,
@DEID int = NULL OUTPUT
AS
BEGIN

  /*
    Adds the specified node to the #tblDOM. If @Tag is specified, but @Text is not specified,
    a single normal node is added. If @Text is specified, then TWO nodes are added: one for
    the specified @Tag, and then a child text node.  (Text nodes have only the TextData and
    the ParentDEID:  they do not have tags or other attributes.)

    If @Tag is null, then only a text node is added.  It is added as a child of the parent that
    was specified.

    HTML comments are a special case.  For these the tag will be !-- and the comment node itself
    will store the comment body in TextData.  TextData will contain the start and end tags
    for the comment (such as <!-- Hello World -->).  There will not be a child text node.

    If @ParentID is specified, #tblDOM is searched for an existing node that has the
    specified HTML ID.  If found, the corresponding ParentDEID will be used as the parent
    for the new node.  Alternately, @ParentDEID may be spedified directly.  If both
    @ParentID and @ParentDEID are null, then the node will be added with a null parent--
    which indicates that it is a top level (or root level) node.

  */
  IF @DocID IS NULL EXEC #spactDOMOpen @DocID = @DocID OUTPUT

  IF @ParentID IS NOT NULL BEGIN
    SELECT @ParentDEID = dom.DEID
    FROM
      #tblDOM dom
    WHERE
      dom.DocID = @DocID AND
      dom.ID = @ParentID
  END

  SET @DEID = NULL

  IF (@Tag IS NOT NULL) BEGIN
    INSERT INTO #tblDOM (
      DocID,
      Tag,
      ID,
      Name,
      Class,
      TextData,
      OpenTagStartPos,
      CloseTagEndPos,
      ParentDEID)
    VALUES (
      @DocID,
      LOWER(@Tag),
      @ID,
      @Name,
      @Class,
      CASE WHEN (@Tag = ''''!--'''') THEN @Text ELSE NULL END,
      @OpenTagStartPos,
      @CloseTagEndPos,
      @ParentDEID)

    SET @DEID = SCOPE_IDENTITY()
    SET @ParentDEID = @DEID

    --Store attributes
    IF ISNULL(RTRIM(@Attribs), '''''''') <> '''''''' BEGIN
      EXEC #spupdDOMAttribs
        @DocID = @DocID OUTPUT,
        @DEID = @DEID,
        @Attribs = @Attribs
    END

  END

  IF (ISNULL(@Tag, '''''''') <> ''''!--'''') AND (@Text IS NOT NULL) BEGIN
    INSERT INTO #tblDOM (
      DocID,
      Tag,
      ID,
      Name,
      Class,
      TextData,
      ParentDEID)
    SELECT
      @DocID,
      NULL AS Tag,
      NULL AS ID,
      NULL AS Name,
      NULL AS Class,
      @Text,
      @ParentDEID
  END


END
'')

/*
**************************************************************************************
PROCEDURE #spupdDOMStyles
Procedure #spupdDOMStyles is to set Styles of existing elements in the DOM
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spupdDOMStyles
@DocID int = NULL,
@DEID int = NULL,
@ID varchar(512) = NULL,
@Name varchar(512),
@Value varchar(MAX)
AS
BEGIN
  IF @DocID IS NULL EXEC #spactDOMOpen @DocID = @DocID OUTPUT

  IF @ID IS NOT NULL BEGIN
    SELECT
      @DEID = dom.DEID
    FROM
      #tblDOM dom
    WHERE
      dom.DocID = @DocID AND
      dom.ID = @ID
  END
  ELSE BEGIN
    SELECT
      @DEID = dom.DEID
    FROM
      #tblDOM dom
    WHERE
      dom.DocID = @DocID AND
      dom.DEID = @DEID
  END

  DECLARE @TargetID int
  SELECT @TargetID = DOMStyleID FROM #tblDOMStyles WHERE DEID = @DEID AND Name = @Name

  IF @TargetID IS NOT NULL BEGIN
    IF @Value IS NULL BEGIN
      DELETE FROM #tblDOMStyles WHERE DOMStyleID = @TargetID
    END
    ELSE BEGIN
      UPDATE #tblDOMStyles SET Value = @Value
      WHERE
        DOMStyleID = @TargetID
    END
  END
  ELSE BEGIN
    INSERT INTO #tblDOMStyles (
      DEID,
      Name,
      Value)
    VALUES (
      @DEID,
      @Name,
      @Value
    )
  END
END
'')

/*
*******************************************************************4*******************
PROCEDURE #spgetDOM
Procedure #spgetDOM is to retrive the internal DOM information as a resultset.
Provides JQuery-like functionality to select nodes from the DOM based on the
specified selector.  The selector can indicate #classes, .id''''s or tags.
If @Selector = NULL, the entire DOM will be returned.
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spgetDOM
@DocID int = NULL OUTPUT,
@Selector varchar(900) = NULL,
@ReturnDEIDsOnly bit = 0
AS
BEGIN
  IF @DocID IS NULL EXEC #spactDOMOpen @DocID = @DocID OUTPUT

  IF @Selector IS NULL BEGIN
    --CTE Start -----------------------
    ;WITH DOMTree (
      DEID,
      DocID,
      Tag,
      ID,
      Name,
      Class,
      TextData,
      OpenTagStartPos,
      CloseTagEndPos,
      ParentDEID,
      HUID,
      SortHUID,
      DOMLevel
    )
    AS
    (
    SELECT
      dom.DEID,
      dom.DocID,
      dom.Tag,
      dom.ID,
      dom.Name,
      dom.Class,
      dom.TextData,
      dom.OpenTagStartPos,
      dom.CloseTagEndPos,
      dom.ParentDEID,
      CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS HUID,
      CAST(RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
      1 AS DOMLevel
    FROM
      #tblDOM dom
    WHERE
      dom.ParentDEID IS NULL

    UNION ALL

    SELECT
      dom.DEID,
      dom.DocID,
      dom.Tag,
      dom.ID,
      dom.Name,
      dom.Class,
      dom.TextData,
      dom.OpenTagStartPos,
      dom.CloseTagEndPos,
      dom.ParentDEID,
      CAST(domch.HUID + ''''.'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS varchar(900)) AS HUID,
      CAST(domch.SortHUID + ''''.'''' + RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
      domch.DOMLevel + 1
    FROM
      DOMTree domch
      JOIN #tblDOM dom ON
        domch.DEID = dom.ParentDEID
    )
    --CTE End -----------------------

    SELECT *
    FROM
      DOMTree dom
    WHERE
      dom.DocID = @DocID
    ORDER BY
      dom.SortHUID

  END
  ELSE BEGIN

    SET @Selector = RTRIM(@Selector) + '''' ''''

    DECLARE @c char
    DECLARE @i int

    DECLARE @Mode varchar(40)
    DECLARE @SelWhere varchar(MAX)
    DECLARE @SelTerm varchar(MAX)


    --default selector is Tag
    SET @Mode = ''''tag''''

    SET @i = 1
    WHILE @i <= LEN(@Selector) BEGIN

      SET @c = SUBSTRING(@Selector, @i, 1)

      IF @c IN (''''.'''', ''''#'''', '''' '''') BEGIN
        IF @c = ''''.'''' BEGIN
          SET @Mode = ''''id''''
        END
        ELSE IF @c = ''''#'''' BEGIN
          SET @Mode = ''''class''''
        END
        ELSE IF @C = '''' '''' BEGIN
          --apply selector
          SET @SelWhere = ISNULL(@SelWhere + '''' AND '''', '''''''') + @SelTerm
        END
        SET @SelTerm = NULL
      END
      ELSE BEGIN
        SET @SelTerm = ISNULL(@SelTerm, '''''''') + @c
      END

      SET @i = @i + 1
    END

    IF @ReturnDEIDsOnly = 1 BEGIN
      IF @Mode = ''''class'''' BEGIN
        SELECT DEID
        FROM
          #tblDOM dom
        WHERE
          dom.DocID = @DocID AND
          dom.Class = @SelTerm
      END
      ELSE IF @Mode = ''''id'''' BEGIN
        SELECT DEID
        FROM
          #tblDOM dom
        WHERE
          dom.DocID = @DocID AND
          dom.ID = @SelTerm
      END
      ELSE IF @Mode = ''''tag'''' BEGIN
        SELECT DEID
        FROM
          #tblDOM dom
        WHERE
          dom.DocID = @DocID AND
          dom.Tag = @SelTerm
      END
    END
    ELSE BEGIN
      IF @Mode = ''''class'''' BEGIN
        --CTE Start -----------------------
        ;WITH DOMTree (
          DEID,
          DocID,
          Tag,
          ID,
          Name,
          Class,
          TextData,
          OpenTagStartPos,
          CloseTagEndPos,
          ParentDEID,
          HUID,
          SortHUID,
          DOMLevel
        )
        AS
        (
        SELECT
          dom.DEID,
          dom.DocID,
          dom.Tag,
          dom.ID,
          dom.Name,
          dom.Class,
          dom.TextData,
          dom.OpenTagStartPos,
          dom.CloseTagEndPos,
          dom.ParentDEID,
          CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS HUID,
          CAST(RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
          1 AS DOMLevel
        FROM
          #tblDOM dom
        WHERE
          dom.ParentDEID IS NULL

        UNION ALL

        SELECT
          dom.DEID,
          dom.DocID,
          dom.Tag,
          dom.ID,
          dom.Name,
          dom.Class,
          dom.TextData,
          dom.OpenTagStartPos,
          dom.CloseTagEndPos,
          dom.ParentDEID,
          CAST(domch.HUID + ''''.'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS varchar(900)) AS HUID,
          CAST(domch.SortHUID + ''''.'''' + RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
          domch.DOMLevel + 1
        FROM
          DOMTree domch
          JOIN #tblDOM dom ON
            domch.DEID = dom.ParentDEID
        )
        --CTE End -----------------------

        SELECT
          dt.DEID,
          dt.DocID,
          dt.Tag,
          dt.ID,
          dt.Name,
          dt.Class,
          dt.TextData,
          dt.OpenTagStartPos,
          dt.CloseTagEndPos,
          dt.ParentDEID,
          dt.HUID,
          dt.SortHUID,
          dt.DOMLevel,
          ROW_NUMBER() OVER (ORDER BY dt.SortHUID) AS Sequence
        FROM
          DomTree dt
          JOIN #tblDOMDocs doc ON
            dt.DocID = doc.DocID
        WHERE
          dt.DocID = @DocID AND
          dt.Class = @SelTerm
        ORDER BY
          dt.sortHUID
      END
      ELSE IF @Mode = ''''id'''' BEGIN
        --CTE Start -----------------------
        ;WITH DOMTree (
          DEID,
          DocID,
          Tag,
          ID,
          Name,
          Class,
          TextData,
          OpenTagStartPos,
          CloseTagEndPos,
          ParentDEID,
          HUID,
          SortHUID,
          DOMLevel
        )
        AS
        (
        SELECT
          dom.DEID,
          dom.DocID,
          dom.Tag,
          dom.ID,
          dom.Name,
          dom.Class,
          dom.TextData,
          dom.OpenTagStartPos,
          dom.CloseTagEndPos,
          dom.ParentDEID,
          CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS HUID,
          CAST(RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
          1 AS DOMLevel
        FROM
          #tblDOM dom
        WHERE
          dom.ParentDEID IS NULL

        UNION ALL

        SELECT
          dom.DEID,
          dom.DocID,
          dom.Tag,
          dom.ID,
          dom.Name,
          dom.Class,
          dom.TextData,
          dom.OpenTagStartPos,
          dom.CloseTagEndPos,
          dom.ParentDEID,
          CAST(domch.HUID + ''''.'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS varchar(900)) AS HUID,
          CAST(domch.SortHUID + ''''.'''' + RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
          domch.DOMLevel + 1
        FROM
          DOMTree domch
          JOIN #tblDOM dom ON
            domch.DEID = dom.ParentDEID
        )
        --CTE End -----------------------
        SELECT
          dt.DEID,
          dt.DocID,
          dt.Tag,
          dt.ID,
          dt.Name,
          dt.Class,
          dt.TextData,
          dt.OpenTagStartPos,
          dt.CloseTagEndPos,
          dt.ParentDEID,
          dt.HUID,
          dt.SortHUID,
          dt.DOMLevel,
          ROW_NUMBER() OVER (ORDER BY dt.SortHUID) AS Sequence
        FROM
          DomTree dt
          JOIN #tblDOMDocs doc ON
            dt.DocID = doc.DocID
        WHERE
          dt.DocID = @DocID AND
          dt.ID = @SelTerm
        ORDER BY
          dt.sortHUID
      END
      ELSE IF @Mode = ''''tag'''' BEGIN
        --CTE Start -----------------------
        ;WITH DOMTree (
          DEID,
          DocID,
          Tag,
          ID,
          Name,
          Class,
          TextData,
          OpenTagStartPos,
          CloseTagEndPos,
          ParentDEID,
          HUID,
          SortHUID,
          DOMLevel
        )
        AS
        (
        SELECT
          dom.DEID,
          dom.DocID,
          dom.Tag,
          dom.ID,
          dom.Name,
          dom.Class,
          dom.TextData,
          dom.OpenTagStartPos,
          dom.CloseTagEndPos,
          dom.ParentDEID,
          CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS HUID,
          CAST(RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
          1 AS DOMLevel
        FROM
          #tblDOM dom
        WHERE
          dom.ParentDEID IS NULL

        UNION ALL

        SELECT
          dom.DEID,
          dom.DocID,
          dom.Tag,
          dom.ID,
          dom.Name,
          dom.Class,
          dom.TextData,
          dom.OpenTagStartPos,
          dom.CloseTagEndPos,
          dom.ParentDEID,
          CAST(domch.HUID + ''''.'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS varchar(900)) AS HUID,
          CAST(domch.SortHUID + ''''.'''' + RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
          domch.DOMLevel + 1
        FROM
          DOMTree domch
          JOIN #tblDOM dom ON
            domch.DEID = dom.ParentDEID
        )
        --CTE End -----------------------
        SELECT
          dt.DEID,
          dt.DocID,
          dt.Tag,
          dt.ID,
          dt.Name,
          dt.Class,
          dt.TextData,
          dt.OpenTagStartPos,
          dt.CloseTagEndPos,
          dt.ParentDEID,
          dt.HUID,
          dt.SortHUID,
          dt.DOMLevel,
          ROW_NUMBER() OVER (ORDER BY dt.SortHUID) AS Sequence
        FROM
          DomTree dt
          JOIN #tblDOMDocs doc ON
            dt.DocID = doc.DocID
          WHERE
            dt.DocID = @DocID AND
            dt.Tag = @SelTerm
          ORDER BY
            dt.sortHUID
      END
    END
  END

END
'')

/*
**************************************************************************************
PROCEDURE #spgetDOMHTML
Procedure #spgetDOMHTML is to render an HTML string based on the internal data in
the DOM
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spgetDOMHTML
@DocID int = NULL OUTPUT,
@ForceDocType varchar(MAX) = NULL,
@PrettyWhitespace bit = 0,
@HTML varchar(MAX) = NULL OUTPUT,
@PrintHTML bit = 1
AS
BEGIN
  DECLARE @Debug bit
  SET @Debug = 0

  IF @DocID IS NULL EXEC #spactDOMOpen @DocID = @DocID OUTPUT

  --temp table to hold local copy of DOM output by #DOM
  CREATE TABLE #Render(
    DEID int PRIMARY KEY,
    DocID int,
    Tag varchar(MAX),
    ID varchar(512),
    Name varchar(512),
    Class varchar(512),
    TextData varchar(MAX),
    OpenTagStartPos int,
    CloseTagEndPos int,
    ParentDEID int,
    HUID varchar(900),
    SortHUID varchar(900),
    DOMLevel int,
    Sequence int,
    HasChild bit
  )

  CREATE INDEX ixtmpRender_SortHUID ON #Render (SortHUID)
  CREATE INDEX ixtmpRender_Sequence ON #Render (Sequence)

  --local table to hold stack of tags
  DECLARE @tvTagStack TABLE (
    StackID int identity PRIMARY KEY, --facilitates deletes
    DEID int,
    CloseTag varchar(900)
  );

  DECLARE @CRLF varchar(5)
  SET @CRLF = CHAR(13) + CHAR(10)

  INSERT INTO #Render (
    DEID,
    DocID,
    Tag,
    ID,
    Name,
    Class,
    TextData,
    OpenTagStartPos,
    CloseTagEndPos,
    ParentDEID,
    HUID,
    SortHUID,
    DOMLevel
  )
  EXEC #spgetDOM @DocID = @DocID OUTPUT

  UPDATE r
  SET
    Sequence = r_seq.Sequence
  FROM
    #Render r
    JOIN (
      SELECT
        r.DEID,
        ROW_NUMBER() OVER (ORDER BY r.SortHUID) AS Sequence
      FROM
        #Render r
      ) r_seq ON
    r.DEID = r_seq.DEID

  UPDATE r
  SET
    HasChild = CASE WHEN r2.DOMLevel > r.DOMLevel THEN 1 ELSE 0 END
  FROM
    #Render r
    JOIN #Render r2 ON
      r.Sequence + 1 = r2.Sequence


  DECLARE curDOM CURSOR LOCAL STATIC FOR
  SELECT
    r.DEID,
    r.Tag,
    r.ID,
    r.Name,
    r.Class,
    r.TextData,
    r.ParentDEID,
    r.HUID,
    r.DOMLevel,
    r.HasChild
  FROM
    #Render r
  ORDER BY
    r.Sequence

  DECLARE @DEID int
  DECLARE @Tag varchar(MAX)
  DECLARE @ID varchar(512)
  DECLARE @Name varchar(512)
  DECLARE @Class varchar(512)
  DECLARE @TextData varchar(MAX)
  DECLARE @ParentDEID int
  DECLARE @HUID varchar(900)
  DECLARE @DOMLevel int
  DECLARE @HasChild bit


  DECLARE @RenderedHTML varchar(MAX)

  DECLARE @DonePop bit
  DECLARE @AllowPush bit

  DECLARE @StackID int
  DECLARE @StackDEID int
  DECLARE @StackTag varchar(MAX)

  DECLARE @EmitTag varchar(MAX)

  DECLARE @ThisStyle varchar(MAX)

  DECLARE @ThisAttribID int
  DECLARE @LastAttribID int
  DECLARE @ThisAttribName varchar(MAX)
  DECLARE @ThisAttribValue varchar(MAX)

  DECLARE @CurParentDEID int
  DECLARE @CurParentTag varchar(MAX)

  OPEN curDOM
  FETCH curDOM INTO
    @DEID,
    @Tag,
    @ID,
    @Name,
    @Class,
    @TextData,
    @ParentDEID,
    @HUID,
    @DOMLevel,
    @HasChild

  SET @RenderedHTML = NULL
  SET @CurParentDEID = NULL
  SET @CurParentTag = NULL
  SET @DonePop = NULL

  WHILE @@FETCH_STATUS = 0 BEGIN
    --Walk through each node of the DOM to render HTML
    SET @ThisStyle = NULL
    SET @ThisAttribID = NULL
    SET @LastAttribID = NULL

    SET @EmitTag = NULL
    SET @AllowPush = NULL

    SET @StackID = NULL
    SET @StackDEID = NULL
    SET @StackTag = NULL

    IF @DonePop IS NULL BEGIN
      --first pass through
      SET @CurParentDEID = @ParentDEID
      SET @CurParentTag = ''''</'''' + @Tag + ''''>''''
      SET @DonePop = 1
    END

    IF @Debug = 1 PRINT ''''Starting node @Tag = '''' + ISNULL(@Tag, ''''NULL'''') +
      '''' @DEID = '''' + ISNULL(CAST(@DEID AS varchar(100)), ''''NULL'''') +
      '''' @ParentDEID = '''' + ISNULL(CAST(@ParentDEID AS varchar(100)), ''''NULL'''') +
      '''' @CurParentDEID = '''' + ISNULL(CAST(@CurParentDEID AS varchar(100)), ''''NULL'''')

    --#1:  See if there is anything we need to pop.  Close tags, set CurParent as needed.
    IF ISNULL(@ParentDEID, 0) <> ISNULL(@CurParentDEID, 0) --AND
   --   (@Tag IS NOT NULL) AND (@Tag NOT LIKE ''''!%'''')
    BEGIN

      IF @Debug = 1 IF @Debug = 1 PRINT ''''TRACE: Need to pop''''

      --need to pop
      SET @DonePop = 0
      WHILE @DonePop = 0 BEGIN

        SET @StackID = NULL
        SET @StackDEID = NULL
        SET @StackTag = NULL

        SELECT TOP 1
          @StackID = StackID,
          @StackDEID = DEID,
          @StackTag = CloseTag
        FROM
          @tvTagStack
        ORDER BY
          StackID DESC

        SET @DonePop = 1

        IF @Debug = 1 IF @Debug = 1 PRINT ''''TRACE: Popped from @CurParentDEID = '''' +
          ISNULL(CAST(@CurParentDEID AS varchar(100)), ''''NULL'''') + '''' to '''' +
          ISNULL(CAST(@StackDEID AS varchar(100)), ''''NULL'''')

        IF @CurParentTag IS NOT NULL BEGIN
          SET @RenderedHTML = ISNULL(@RenderedHTML + CASE WHEN @PrettyWhitespace = 1 THEN @CRLF ELSE '''''''' END, '''''''') + @CurParentTag
        END

        SET @CurParentDEID = @StackDEID
        SET @CurParentTag = @StackTag

        --Note:  CurParent is left open.  May be re-pushed

        IF @StackID IS NULL BEGIN
          SET @DonePop = 1
        END
        ELSE BEGIN
          DELETE FROM @tvTagStack WHERE StackID = @StackID

          IF ISNULL(@ParentDEID, 0) <> ISNULL(@CurParentDEID, 0) BEGIN
            SET @DonePop = 0
            --render close tag
--            SET @RenderedHTML = ISNULL(@RenderedHTML + CASE WHEN @PrettyWhitespace = 1 THEN @CRLF ELSE '''''''' END, '''''''') + @StackTag
          END

        END

      END --WHILE @DonePop = 0
    END  --IF CurParent change needed


    IF ISNULL(@ParentDEID, 0) <> ISNULL(@CurParentDEID, 0) BEGIN
      PRINT ''''Error in DOM:  could not pop back to where @ParentDEID = @CurParentDEID '''' +
      ''''(@ParentDEID = '''' + ISNULL(CAST(@ParentDEID AS varchar(100)), ''''NULL'''') +
      '''' @CurParentDEID = '''' + ISNULL(CAST(@CurParentDEID AS varchar(100)), ''''NULL'''') + '''')''''
    END


    --#2: Render tag
    IF @Tag = ''''!--'''' BEGIN
      --HTML Comment
      SET @RenderedHTML = ISNULL(@RenderedHTML + CASE WHEN @PrettyWhitespace = 1 THEN @CRLF ELSE '''''''' END, '''''''') + ISNULL(@TextData, '''''''')
      SET @AllowPush = 0
    END
    ELSE IF @Tag LIKE ''''!%'''' BEGIN
      --declaration
      SET @RenderedHTML = ISNULL(@RenderedHTML + CASE WHEN @PrettyWhitespace = 1 THEN @CRLF ELSE '''''''' END, '''''''') + ISNULL(''''<'''' + @TextData + ''''>'''', '''''''')
      SET @AllowPush = 0
    END
    ELSE IF @Tag IS NULL BEGIN
      --text node
      SET @RenderedHTML = ISNULL(@RenderedHTML + CASE WHEN @PrettyWhitespace = 1 THEN @CRLF ELSE '''''''' END, '''''''') + ISNULL(@TextData, '''''''')
      SET @AllowPush = 0
    END
    ELSE BEGIN
      --normal node
      SET @AllowPush = 1

      SET @EmitTag = ''''<'''' + @Tag +
        ISNULL('''' id="'''' + @ID + ''''"'''', '''''''') +
        ISNULL('''' name="'''' + @Name + ''''"'''', '''''''') +
        ISNULL('''' class="'''' + @Class + ''''"'''', '''''''')

      IF EXISTS (SELECT DOMStyleID FROM #tblDOMStyles WHERE DEID = @DEID) BEGIN
        SET @ThisAttribID = -1
        WHILE @ThisAttribID IS NOT NULL BEGIN
          SET @ThisAttribID = NULL
          SELECT TOP 1
            @ThisAttribID = da.DOMStyleID,
            @ThisAttribName = da.Name,
            @ThisAttribValue = da.Value
          FROM
            #DOMStyles da
          WHERE
            da.DEID = @DEID AND
            da.DOMStyleID > ISNULL(@LastAttribID, 0)
          ORDER BY
            da.DOMStyleID

          IF @ThisAttribID IS NOT NULL BEGIN
            SET @ThisStyle = ISNULL(@ThisStyle, '''''''') + ISNULL(@ThisAttribName + '''': '''' + @ThisAttribValue + '''';'''', '''''''')
          END

          SET @LastAttribID = @ThisAttribID
        END
      END

      --save list of styles in style attribute
      EXEC #spupdDOMAttribs @DocID = @DocID OUTPUT, @DEID = @DEID, @Name = ''''style'''', @Value = @ThisStyle

      IF EXISTS (SELECT DOMAttribID FROM #tblDOMAttribs WHERE DEID = @DEID) BEGIN
        SET @ThisAttribID = -1
        WHILE @ThisAttribID IS NOT NULL BEGIN
          SET @ThisAttribID = NULL
          SELECT TOP 1
            @ThisAttribID = da.DOMAttribID,
            @ThisAttribName = da.Name,
            @ThisAttribValue = da.Value
          FROM
            #tblDOMAttribs da
          WHERE
            da.DEID = @DEID AND
            da.DOMAttribID > ISNULL(@LastAttribID, 0)
          ORDER BY
            da.DOMAttribID

          IF @ThisAttribID IS NOT NULL BEGIN
            SET @EmitTag = @EmitTag + ISNULL('''' '''' + @ThisAttribName + ''''="'''' + @ThisAttribValue + ''''"'''', '''''''')
          END

          SET @LastAttribID = @ThisAttribID
        END
      END

      SET @RenderedHTML = ISNULL(@RenderedHTML + CASE WHEN @PrettyWhitespace = 1 THEN @CRLF ELSE '''''''' END, '''''''') + @EmitTag +
        CASE WHEN @HasChild = 0 THEN ''''/'''' ELSE '''''''' END + ''''>''''
    END


    --#3: Set CurParentDEID = new node, if applicable
    IF (@AllowPush = 1) AND (@HasChild = 1) BEGIN
      --push and move CurParent to newly-inserted node

      IF @CurParentDEID IS NOT NULL BEGIN
        INSERT INTO @tvTagStack (
          DEID,
          CloseTag
        )
        VALUES (
          @CurParentDEID,
          @CurParentTag
        )
      END

      IF @Debug = 1 IF @Debug = 1 PRINT ''''TRACE: Push @CurParentDEID = '''' + ISNULL(CAST(@CurParentDEID AS varchar(100)), ''''NULL'''') +
       '''' New @CurParentDEID = '''' +  ISNULL(CAST(@DEID AS varchar(100)), ''''NULL'''')

      SET @CurParentDEID = @DEID
      SET @CurParentTag =  ''''</'''' + @Tag + ''''>''''

    END


    FETCH curDOM INTO
      @DEID,
      @Tag,
      @ID,
      @Name,
      @Class,
      @TextData,
      @ParentDEID,
      @HUID,
      @DOMLevel,
      @HasChild
  END
  CLOSE curDOM

  IF @CurParentTag IS NOT NULL BEGIN
    SET @RenderedHTML = ISNULL(@RenderedHTML + CASE WHEN @PrettyWhitespace = 1 THEN @CRLF ELSE '''''''' END, '''''''') + @CurParentTag
  END

  WHILE EXISTS(SELECT StackID FROM @tvTagStack) BEGIN

    SELECT TOP 1
      @StackID = StackID,
      @StackDEID = DEID,
      @StackTag = CloseTag
    FROM
      @tvTagStack
    ORDER BY
      StackID DESC

    DELETE FROM @tvTagStack WHERE StackID = @StackID

    SET @RenderedHTML = ISNULL(@RenderedHTML + CASE WHEN @PrettyWhitespace = 1 THEN @CRLF ELSE '''''''' END, '''''''') + ISNULL(@StackTag, '''''''')
  END

  SET @HTML = @RenderedHTML

  DROP TABLE #Render

  IF @PrintHTML = 1 BEGIN
    PRINT @HTML
  END
END
'')

/*
**************************************************************************************
PROCEDURE #spactDOMLoad
Procedure #spactDOMLoad parses the provided @HTML and loads into DOM.

If @ID or @DEID is specified, modifes existing DOM starting with the specified node.

If @Selector is specified, the #Load operation will be performed for each node
that matches the specified selection.

If @ReplaceOuter = 1 the specified node itself will also be replaced (i.e. OUTER HTML),
otherwise only the children of the specified node will be replaced (i.e. INNER
HTML)

If neither @ID or @DEID is specified, clears entire DOM and loads from @HTML.

@Attribs may specify a string of Attributes that will be appended to every node
affected by #spactDOMLoad.

IF @Class is specifed,
**************************************************************************************
*/
exec(''
CREATE PROCEDURE #spactDOMLoad
@DocID int = NULL,
@HTML varchar(MAX),
@ID varchar(512) = NULL,
@DEID int = NULL,
@ReplaceOuter bit = 0,
@CreateNew bit = 0,

@Selector varchar(MAX) = NULL,
@IncludeAllWhitespace bit = 0,
@Tolerate bit = 0
AS
BEGIN
  EXEC #spactDOMOpen @CreateNew = @CreateNew, @DocID = @DocID OUTPUT


  --local table to hold stack of tags
  DECLARE @tvTagStack TABLE (
    TagStackID int identity PRIMARY KEY ,
    Tag varchar(512),
    DEID int,
    ParentDEID int
  );

  DECLARE @tvTargetList TABLE (
    DEID int PRIMARY KEY
  )


  DECLARE @TargetDEID int

  IF @ID IS NOT NULL BEGIN
    SELECT
      @DEID = dom.DEID
    FROM
      #tblDOM dom
    WHERE
      dom.DocID = @DocID AND
      dom.ID = @ID
  END
  ELSE IF @Selector IS NOT NULL BEGIN
    INSERT INTO @tvTargetList (DEID)
    EXEC #spgetDOM @DocID = @DocID OUTPUT, @Selector = @Selector, @ReturnDEIDsOnly = 1
    SELECT TOP 1 @DEID = DEID FROM @tvTargetList
  END


  DECLARE @i int
  DECLARE @c char

  DECLARE @IsSingleton bit
  DECLARE @InComment bit
  DECLARE @InQuote bit
  DECLARE @StartQuote char

  DECLARE @ParentDEID int
  DECLARE @LastDEID int

  DECLARE @TopStackID int
  DECLARE @StackTag varchar(8000)
  DECLARE @PopDone bit

  DECLARE @State varchar(40)
  DECLARE @OpenTagName varchar(512)
  DECLARE @CloseTagName varchar(512)

  DECLARE @Text varchar(MAX)
  DECLARE @AttribStr varchar(MAX)
  DECLARE @CommentStr varchar(MAX)

  DECLARE @TextChunk varchar(8000)
  DECLARE @AttribChunk varchar(8000)

  DECLARE @StartPos int
  DECLARE @EndPos int
  DECLARE @CommentStartPos int
  DECLARE @TextLen int
  DECLARE @TextChunkLen int

  DECLARE @DoOpenTag bit
  DECLARE @DoCloseTag bit
  DECLARE @ImmediateClose bit


  IF (@DEID IS NULL) AND (@Selector IS NULL) BEGIN
    EXEC #spactDOMClear @DocID = @DocID OUTPUT
    SET @DEID = -1
  END

  WHILE @DEID IS NOT NULL BEGIN
    SET @Text = ''''''''
    SET @CommentStr = ''''''''
    SET @LastDEID = NULL
    SET @ParentDEID = NULL
    SET @OpenTagName = NULL
    SET @CloseTagName = NULL

    SET @ImmediateClose = 0
    SET @IsSingleton = 0
    SET @InComment = 0
    SET @InQuote = 0
    SET @StartQuote = NULL

    SET @StartPos = NULL
    SET @EndPos = NULL
    SET @CommentStartPos = NULL

    SET @TextChunk = ''''''''
    SET @AttribChunk = ''''''''

    SET @Text = ''''''''
    SET @AttribStr = ''''''''
    SET @CommentStr = ''''''''

    SELECT
      @ParentDEID = CASE WHEN @ReplaceOuter = 1 THEN dom.ParentDEID ELSE dom.DEID END
    FROM
      #tblDOM dom
    WHERE
      dom.DocID = @DocID AND
      dom.DEID = @DEID

    --Note:  we are replacing all child nodes.  We might be replacing
    --the target node too--if @ReplaceOuter = 1

    IF @HTML IS NOT NULL BEGIN
      DELETE FROM #tblDOM
      WHERE
        (((@ReplaceOuter = 1 ) AND (DEID = @DEID)) OR
         ((ParentDEID = @DEID) AND (LEFT(@HTML, 1) = ''''<'''')))

      SET @i = 1

      SET @OpenTagName = ''''''''
      SET @CloseTagName = ''''''''

      SET @State = ''''Text''''

      WHILE @i <= LEN(@HTML) BEGIN
        SET @c = SUBSTRING(@HTML, @i, 1)

        --IF @State = ''''Comment'''' BEGIN
        IF @InComment = 1 BEGIN
          --special case:  locked in processing text until -->
          SET @CommentStr = @CommentStr + @c

          IF PATINDEX(''''%-->%'''', @CommentStr) > 0 BEGIN
            --reached the end of the comment
            EXEC #spinsDOMNode
              @DocID = @DocID OUTPUT,
              @Tag = ''''!--'''',
              @Text = @CommentStr,
              @OpenTagStartPos = @CommentStartPos,
              @CloseTagEndPos = @i,
              @ParentDEID = @ParentDEID

            SET @CommentStr = ''''''''
            SET @CommentStartPos = 0

            SET @State = ''''Text''''
            SET @InComment = 0
            --SET @i = @i + 1
          END
        END
        ELSE BEGIN
          IF (@i = LEN(@HTML)) AND
             ((@IncludeAllWhitespace = 1) OR
              (@C NOT IN (CHAR(9), CHAR(10), CHAR(13), '''' ''''))) BEGIN

            --at the last character of our @HTML
            IF @IncludeAllWhitespace = 1 BEGIN
              SET @TextLen = LEN(@Text + ''''x'''') - 1
              SET @TextChunkLen = LEN(@TextChunk + ''''x'''') - 1
            END
            ELSE BEGIN
              EXEC #spgetLenNTW @s = @Text, @Len = @TextLen OUTPUT
              EXEC #spgetLenNTW @s = @TextChunk, @Len = @TextChunkLen OUTPUT
            END

            IF (@TextLen > 0) OR (@TextChunkLen > 0) BEGIN
              --special case of text-only @HTML (no tags)
              SET @TextChunk = @TextChunk + @c
              IF @TextChunk <> '''''''' BEGIN
                SET @Text = @Text + @TextChunk
                SET @TextChunk = ''''''''
              END

              EXEC #spinsDOMNode
                @DocID = @DocID OUTPUT,
                @Tag = NULL,
                @Text = @Text,
                @ParentDEID = @ParentDEID,
                @DEID = @LastDEID OUTPUT

              SET @Text = ''''''''
            END
          END

          --special occurrences of / Note that these could have been coded to
          --be handled below in each respective State, but seemed more clear to
          --keep together here.
          ELSE IF (@c = ''''/'''') AND (@State = ''''StartTag'''') BEGIN
            SET @State = ''''CloseTagName''''
          END
          ELSE IF (@c = ''''/'''') AND (@State = ''''OpenTagName'''') BEGIN
            --Immediate close of tag.  Actual close will happen on >
            SET @ImmediateClose = 1
          END
          ELSE IF (@c = ''''/'''') AND (@State = ''''CloseTagName'''') BEGIN
            --NOOP:  we want to drop the /
            SET @c = @c
          END
          ELSE IF (@c = ''''/'''') AND (@State = ''''Attributes'''') AND (@InQuote = 0) BEGIN
            IF @Tolerate = 1 BEGIN
              IF SUBSTRING(@HTML, @i + 1, 1) <> ''''>'''' BEGIN
                --False alarm:  HTML is missing quotes around attribute values.
                --This is not really an indication of the end of the tag.

                SET @AttribChunk = @AttribChunk + @c
                IF LEN(@AttribChunk) = 8000 BEGIN
                  SET @AttribStr = @AttribStr + @AttribChunk
                  SET @AttribChunk = ''''''''
                END
              END
              ELSE BEGIN
                SET @State = ''''OpenTagName''''
                SET @ImmediateClose = 1
              END
            END
            ELSE BEGIN
              SET @State = ''''OpenTagName''''
              SET @ImmediateClose = 1
            END

          END


          ELSE IF (@c = ''''<'''') BEGIN
            SET @StartPos = @i

            IF @TextChunk <> '''''''' BEGIN
             SET @Text = @Text + @TextChunk
              SET @TextChunk = ''''''''
            END

            IF @Text  <> '''''''' BEGIN
              --reached the end of the text node
              EXEC #spgetLenNTW @s = @Text, @Len = @TextLen OUTPUT

              IF ((@IncludeAllWhitespace = 1) OR (@TextLen > 0)) BEGIN
                EXEC #spinsDOMNode
                  @DocID = @DocID OUTPUT,
                  @Tag = NULL,
                  @Text = @Text,
                  @ParentDEID = @ParentDEID,
                  @DEID = @LastDEID OUTPUT

                SET @Text = ''''''''
              END
            END

            --See if we are starting a comment
            IF SUBSTRING(@HTML, @i, LEN(''''<!--'''')) = ''''<!--'''' BEGIN
              --SET @State = ''''Comment''''
              SET @InComment = 1
              SET @CommentStr = @c
              SET @CommentStartPos = @i
            END
            ELSE BEGIN
              --otherwise we are just starting a new tag
              SET @State = ''''StartTag''''
            END
          END

          ELSE IF (@c = ''''>'''') BEGIN
            IF @State = ''''CloseTagName'''' BEGIN
              SET @EndPos = @i
              SET @IsSingleton = CASE WHEN
                (@CloseTagName IN (''''area'''', ''''br'''', ''''col'''', ''''command'''', ''''embed'''', ''''hr'''', ''''img'''', ''''input'''', ''''link'''', ''''meta'''', ''''param'''', ''''source'''')) OR
                (@CloseTagName LIKE ''''!%'''') THEN 1 ELSE 0 END

              IF @IsSingleton = 0 BEGIN
                --Not a singleton HTML tag for which we ignore the close tag if present
                SET @DoCloseTag = 1
              END
            END
            ELSE IF @State IN (''''OpenTagName'''', ''''Attributes'''') BEGIN
              SET @IsSingleton = CASE WHEN
               (@OpenTagName IN (''''area'''', ''''br'''', ''''col'''', ''''command'''', ''''embed'''', ''''hr'''', ''''img'''', ''''input'''', ''''link'''', ''''meta'''', ''''param'''', ''''source'''')) OR
               (@OpenTagName LIKE ''''!%'''') THEN 1 ELSE 0 END

              IF @IsSingleton = 1 BEGIN
                --Singleton HTML tag that does not need to be closed
                SET @ImmediateClose = 1
              END
              SET @DoOpenTag = 1
            END
          END

          ELSE IF @State = ''''StartTag'''' BEGIN
            --not a / because that case was handled above
            SET @State = ''''OpenTagName''''
            SET @OpenTagName = @c
          END
          ELSE IF @State = ''''OpenTagName'''' BEGIN
            IF @c = '''' '''' BEGIN
              SET @State = ''''Attributes''''
            END
            ELSE BEGIN
              --Not a / because that case was handled above
              SET @OpenTagName = @OpenTagName + @c
            END
          END
          ELSE IF @State = ''''CloseTagName'''' BEGIN
            SET @CloseTagName = @CloseTagName + @c
          END
          ELSE IF @State = ''''Attributes'''' BEGIN
            --not a / because that case was handled above
            IF (@c IN (''''"'''', '''''''''''''''')) BEGIN
              IF (@InQuote = 0) AND ((@StartQuote IS NULL) OR (@c = @StartQuote)) BEGIN
                SET @InQuote = 1
                IF @StartQuote IS NULL BEGIN
                  SET @StartQuote = @c
                END
              END
              ELSE IF (@InQuote = 1) AND (@c = @StartQuote) BEGIN
                SET @InQuote = 0
              END
            END

            SET @AttribChunk = @AttribChunk + @c
            IF LEN(@AttribChunk) = 8000 BEGIN
              SET @AttribStr = @AttribStr + @AttribChunk
              SET @AttribChunk = ''''''''
            END

          END

          ELSE IF @State IN (''''Text'''') BEGIN


            SET @TextChunk = @TextChunk + @c



            IF LEN(@TextChunk) = 8000 BEGIN
             SET @Text = @Text + @TextChunk
              SET @TextChunk = ''''''''
            END

          END
          ELSE BEGIN
            RAISERROR(''''Error in #Load:  Unexpected state parsing HTML'''', 16, 1)
          END

          --Processing for completed OpenTag
          IF @DoOpenTag = 1 BEGIN

            SET @DoOpenTag = 0

            IF @AttribChunk <> '''''''' BEGIN
              SET @AttribStr = @AttribStr + @AttribChunk
              SET @AttribChunk = ''''''''
            END

            IF @ImmediateClose = 1 BEGIN
              SET @EndPos = @i --should be called on the >
            END

            IF @OpenTagName = ''''script'''' BEGIN
              --A special case:  we know that there must be an end tag for the script
              --(required in all cases), and we know we don''''t want to inspect the contents
              --of the script block.  So we can copy the whole block at once here and
              --save some looping and concatenating.

              --SET @Text = RIGHT(@HTML, LEN(@HTML) - @i)
              --SET @Text = SUBSTRING(@Text, 1, PATINDEX(''''%</script>%'''', @Text) - 1)
              --SET @Text = SUBSTRING(RIGHT(@HTML, LEN(@HTML) - @i), 1, PATINDEX(''''%</script>%'''', RIGHT(@HTML, LEN(@HTML) - @i)) - 1)
              SET @EndPos = PATINDEX(''''%</script>%'''', RIGHT(@HTML, LEN(@HTML) - @i)) - 1
              SET @Text = SUBSTRING(RIGHT(@HTML, LEN(@HTML) - @i), 1, @EndPos)

              SET @i = @i + LEN(@Text) + LEN(''''</script>'''')
              SET @EndPos = @i

              EXEC #spinsDOMNode
                @DocID = @DocID OUTPUT,
                @Tag = @OpenTagName,
                @Attribs = @AttribStr,
                @Text = @Text,
                @OpenTagStartPos = @StartPos,
                @CloseTagEndPos = @EndPos,
                @ParentDEID = @ParentDEID,
                @DEID = @LastDEID OUTPUT

                SET @Text = ''''''''
            END
            ELSE BEGIN
              EXEC #spinsDOMNode
                @DocID = @DocID OUTPUT,
                @Tag = @OpenTagName,
                @Attribs = @AttribStr,
                @Text = NULL, --@Text,
                @OpenTagStartPos = @StartPos,
                @CloseTagEndPos = @EndPos,
                @ParentDEID = @ParentDEID,
                @DEID = @LastDEID OUTPUT
              END

            IF @ImmediateClose = 1 BEGIN
              SET @ImmediateClose = 0
              --Note:  do not change @ParentDEID
            END
            ELSE BEGIN
              IF @IsSingleton = 0 BEGIN
                --Note:  Comments, declarations and singleton tags should never be a parent,
                --and so they don''''t get pushed onto the stack.

                --Push tag
                INSERT INTO @tvTagStack (Tag, DEID, ParentDEID)
                VALUES (@OpenTagName, @LastDEID, @ParentDEID)

                SET @ParentDEID = @LastDEID
              END
            END


            SET @State = ''''Text''''

            SET @OpenTagName = ''''''''
            SET @AttribStr = ''''''''
            SET @AttribChunk = ''''''''
            SET @Text = ''''''''
            SET @TextChunk = ''''''''

          END


          --Processing for completed CloseTag
          IF @DoCloseTag = 1 BEGIN
            SET @DoCloseTag = 0

            --Pop tag
            IF @IsSingleton = 0 BEGIN
              --not a singleton tag

              SET @StackTag = ''''''''
              SET @PopDone = 0

              SELECT TOP 1 @TopStackID = TagStackID FROM @tvTagStack ORDER BY TagStackID DESC

              WHILE (@TopStackID IS NOT NULL) AND
                    (@StackTag <> @CloseTagName) AND
                    (@PopDone = 0) BEGIN

                /*
                Note:  The idea is that we pushed nodes onto a stack.  We have reached the closing tag for a
                node, and so now we want to pop off all nodes that were pushed until we pop off the corresponding
                opening tag.

                There could be a problem is with non-XHTML:  In XMHTML, tags such as <td> and <li> must be
                closed--as they should be--because they can contain child text nodes.  However, the HTML spec
                allows for <td> and <li> to be pseudo-singletons...meaning that they may not have a closing tag.

                Consequently, the current behavior is that since there is no closing tag (i.e. </td>) on the stack,
                we will keep poping until we come to the top of the stack.  Thus the next tag after the </td>--which
                will likely be a <td> in this case--will be inserted as a root-level node with no parent.

                This behavior is not bad:  it is fairly fault-tollerant.  The nodes will still be processed, and the
                sequence of the nodes will still be presevered.

                Nonetheless, a future enhancement might be to somehow limit the popping to stop at the "inferred"
                parent.  For example, we know that the parent of a <td> should be a <tr>.  So perhaps stopping popping
                when we reach the <tr> is possible through some yet-to-be-defined means.
                */

                SET @TopStackID = NULL
                SET @StackTag = NULL

                SELECT TOP 1
                  @TopStackID = ts.TagStackID,
                  @StackTag = ts.Tag,
                  @LastDEID = ts.DEID,
                  @ParentDEID = ts.ParentDEID
                FROM
                  @tvTagStack ts
                ORDER BY
                  ts.TagStackID DESC

                DELETE FROM @tvTagStack WHERE TagStackID = @TopStackID

              END
            END

            UPDATE #tblDOM
            SET CloseTagEndPos = @EndPos
            WHERE
              DEID = @LastDEID

            SET @CloseTagName = ''''''''

            SET @State = ''''Text''''
            SET @Text = ''''''''

          END

        END

        SET @i = @i + 1
      END
    END

    DELETE FROM @tvTargetList WHERE DEID = @DEID

    SET @DEID = NULL

    IF EXISTS(SELECT DEID FROM @tvTargetList) BEGIN
      SELECT TOP 1 @DEID = DEID FROM @tvTargetList
    END
  END

END
'')

exec(''
CREATE PROCEDURE #sputilGetHTTP
@URL varchar(MAX),
  --URL to retrieve data from
@HTTPMethod varchar(40) = ''''GET'''',
  --can be either GET or POST
@ContentType varchar(80)= ''''text/http'''',
@DataToSend nvarchar(4000) = NULL,

@HTTPStatus int = NULL OUTPUT,
  --HTTP Status Code (200=OK, 404=Not Found, etc.)
@ResponseText nvarchar(MAX) = NULL OUTPUT,
  --Full text returned by remote HTTP server (if @SuppressResponseText = 0)

@StartTag varchar(100) = NULL, --''''<div class="entry-content">'''',
  --Token to mark start of block to return in @ParsedText.  NULL means return nothing.
@EndTag varchar(100) =  NULL, --''''</div><!-- .entry-content -->'''',
@IncludeStartTag bit = 1,
@IncludeEndTag bit = 1,

  --Token to mark end of block to return in @ParsedText.  NULL means return to end.
@ParsedText nvarchar(MAX) = NULL OUTPUT,
  --Substring of @ResponseText delimeted by @StartTag and @EndTag

@ErrorMsg varchar(MAX) = NULL OUTPUT,
  --NULL unless an error message was encountered
@LastResultCode int = NULL OUTPUT,
  --0 unless an error code was returned by MSXML2.ServerXMLHttp

@SuppressResponseText bit = 0,
  --If 0, actual content is not returned from remote server (just status code)
@SuppressResultset bit = 1
  --If 0, result set is is not returned (just parameters)
AS
BEGIN
  --Retrieves data via HTTP

  --http://msdn.microsoft.com/en-us/library/aa238861(v=sql.80).aspx

  SET NOCOUNT ON

  DECLARE @Debug bit
  SET @Debug = 0

  DECLARE @CRLF char(5)
  SET @CRLF = CHAR(13) + CHAR(10)

  DECLARE @XML xml
  DECLARE @Obj int

  DECLARE @PerformedInit bit
  SET @PerformedInit = 0

  DECLARE @ErrSource varchar(512)
  DECLARE @ErrMsg varchar(512)

  DECLARE @tvResponse TABLE (Response nvarchar(MAX))

  BEGIN TRY
    IF @Debug = 1 PRINT ''''About to call sp_OACreate for MSXML2.ServerXMLHttp''''

    EXEC @LastResultCode = sp_OACreate ''''MSXML2.ServerXMLHttp'''', @Obj OUT
    IF @LastResultCode <> 0 BEGIN
      EXEC sp_OAGetErrorInfo @obj, @ErrSource OUTPUT, @ErrMsg OUTPUT
      PRINT @ErrSource
      PRINT @ErrMsg
    END
    ELSE BEGIN
      SET @PerformedInit = 1
    END

    IF @LastResultCode = 0 BEGIN
      IF @HTTPMethod = ''''GET'''' BEGIN

       IF @Debug = 1 PRINT ''''About to call sp_OAMethod for open (GET)''''
        EXEC @LastResultCode = sp_OAMethod @Obj, ''''open'''', NULL, ''''GET'''', @URL, false
        IF @LastResultCode <> 0 BEGIN
          EXEC sp_OAGetErrorInfo @obj, @ErrSource OUTPUT, @ErrMsg OUTPUT
          PRINT @ErrSource
          PRINT @ErrMsg
        END

      END
      ELSE BEGIN
       IF @Debug = 1 PRINT ''''About to call sp_OAMethod for open (POST)''''
        EXEC @LastResultCode = sp_OAMethod @Obj, ''''open'''', NULL, ''''POST'''', @URL, false
        IF @LastResultCode <> 0 BEGIN
          EXEC sp_OAGetErrorInfo @obj, @ErrSource OUTPUT, @ErrMsg OUTPUT
          PRINT @ErrSource
          PRINT @ErrMsg
        END

        IF @Debug = 1 PRINT ''''About to call sp_OAMethod for setRequestHeader''''
        IF @LastResultCode = 0 EXEC @LastResultCode = sp_OAMethod @Obj, ''''setRequestHeader'''', NULL, ''''Content-Type'''', @ContentType
        IF @LastResultCode <> 0 BEGIN
          EXEC sp_OAGetErrorInfo @obj, @ErrSource OUTPUT, @ErrMsg OUTPUT
          PRINT @ErrSource
          PRINT @ErrMsg
        END

      END
    END

    IF @Debug = 1 PRINT ''''About to call sp_OAMethod for send''''
    IF @LastResultCode = 0 EXEC @LastResultCode = sp_OAMethod @Obj, ''''send'''', NULL, @DataToSend
    IF @LastResultCode <> 0 BEGIN
      EXEC sp_OAGetErrorInfo @obj, @ErrSource OUTPUT, @ErrMsg OUTPUT
      PRINT @ErrSource
      PRINT @ErrMsg
    END

    IF @LastResultCode = 0 EXEC @LastResultCode = sp_OAGetProperty @Obj, ''''status'''', @HTTPStatus OUT
    IF @LastResultCode <> 0 BEGIN
      EXEC sp_OAGetErrorInfo @obj, @ErrSource OUTPUT, @ErrMsg OUTPUT
      PRINT @ErrSource
      PRINT @ErrMsg
    END

    IF (@LastResultCode = 0) AND (ISNULL(@SuppressResponseText, 0) = 0) BEGIN
      INSERT INTO @tvResponse (Response)
      EXEC @LastResultCode = sp_OAGetProperty @Obj, ''''responseText'''' --, @Response OUT
        --Note:  sp_OAGetProperty (or any extended stored procedure parameter) does not support
        --varchar(MAX), however returning as a resultset will return long results.
    END
  END TRY
  BEGIN CATCH
   SET @ErrorMsg = ERROR_MESSAGE() +
     ISNULL(@CRLF + @ErrMsg, '''''''')
  END CATCH

  BEGIN TRY
    DECLARE @DestroyResultCode int
    EXEC @DestroyResultCode = sp_OADestroy @Obj
  END TRY
  BEGIN CATCH
    SET @ErrorMsg = ISNULL(@ErrorMsg + @CRLF, '''''''') + ERROR_MESSAGE() +
     ''''on call to sp_OADestroy.''''
  END CATCH

  SELECT @ResponseText = Response FROM @tvResponse

  IF (@LastResultCode = 0) AND (@StartTag IS NOT NULL) BEGIN
    DECLARE @P1 int
    DECLARE @P2 int

    SET @P1 = PATINDEX(''''%'''' + @StartTag +''''%'''', @ResponseText)
    IF @IncludeEndTag = 0 SET @P1 = @P1 + LEN(@EndTag + ''''x'''') - 1

    IF @EndTag IS NULL BEGIN
      SET @P2 = LEN(@ResponseText + ''''x'''') - 1
    END
    ELSE BEGIN
      SET @P2 = PATINDEX(''''%'''' + @EndTag + ''''%'''', @ResponseText) - 1
      IF @IncludeEndTag = 1 SET @P2 = @P2 + LEN(@EndTag + ''''x'''') - 1
    END

    --SET @ParsedText = REPLACE(tempdb.dbo.RTRIMWhitespace(tempdb.dbo.LTRIMWhitespace(SUBSTRING(@ResponseText, @P1, @P2 - @P1 + 1))), CHAR(9), '''''''')

    SET @ParsedText = SUBSTRING(@ResponseText, @P1, @P2 - @P1 + 1)
    EXEC #spactTrimWhitespace @S = @ParsedText OUTPUT, @DoLeft = 1, @DoRight = 1, @TrimTabs = 1
  END


  IF ISNULL(@SuppressResultset, 0) = 0 BEGIN
    SELECT
      @URL AS URL,
      @ResponseText AS ResponseText,
      @ParsedText AS ParsedText,
      @HTTPStatus AS HTTPStatus,
      @LastResultCode AS LastResultCode,
      @ErrorMsg AS ErrorMsg
  END

  IF ((ISNULL(@LastResultCode, -1) <> 0) OR
      (ISNULL(@DestroyResultCode, -1) <> 0) OR
      (@ErrorMsg IS NOT NULL)) BEGIN
    SET @ErrorMsg = ''''Error in #sputlGetHTTP: '''' + @CRLF +
      ISNULL(NULLIF(RTRIM(@ErrMsg), '''''''') + @CRLF, '''''''') +
--      ISNULL(NULLIF(RTRIM(@ErrSource), '''''''') + @CRLF, '''''''') +
      ISNULL(NULLIF(RTRIM(@ErrorMsg), '''''''') + @CRLF, '''''''') +
      CASE WHEN @PerformedInit = 0 THEN
      @CRLF +
      ''''Remember that this stored procedure uses OLE.  To work properly you may need to configure '''' +
      ''''your database to allow OLE, as follows: '''' + @CRLF +
      ''''  EXEC sp_configure ''''''''show advanced options'''''''', 1;'''' + @CRLF +
      ''''  RECONFIGURE;'''' + @CRLF +
      ''''  EXEC sp_configure ''''''''Ole Automation Procedures'''''''', 1;'''' + @CRLF +
      ''''  RECONFIGURE;'''' + @CRLF +
      ''''Also, your SQL user must have execute rights to the following stored procedures in master:'''' + @CRLF +
      ''''  sp_OACreate'''' + @CRLF +
      ''''  sp_OAGetProperty'''' + @CRLF +
      ''''  sp_OASetProperty'''' + @CRLF +
      ''''  sp_OAMethod'''' + @CRLF +
      ''''  sp_OAGetErrorInfo'''' + @CRLF +
      ''''  sp_OADestroy'''' + @CRLF +
      ''''You can grant rights for each of these as follows:'''' + @CRLF +
      ''''  USE master'''' + @CRLF +
      ''''  GRANT EXEC ON sp_OACreate TO myuser'''' + @CRLF +
      ''''  GRANT EXEC etc. ...''''
      ELSE '''''''' END

      RAISERROR(@ErrorMsg, 16, 1)
  END

END
'')
--------------------
exec(''
CREATE PROCEDURE #sputilConvertJSONToXML
@JSON nvarchar(MAX),
@XML xml OUTPUT
AS
BEGIN
  DECLARE @tvStack TABLE (
    StackID int IDENTITY PRIMARY KEY,
    Tag varchar(8000),
    IsArrayElem bit
  )

  DECLARE @I int
  DECLARE @C char
  DECLARE @LastChar char

  DECLARE @Buf varchar(8000)
  DECLARE @XMLStr varchar(MAX)
  DECLARE @Tag varchar(8000)

  DECLARE @StackID int

  DECLARE @InQuote bit
  DECLARE @EndedQuote bit
  DECLARE @IsArrayElem bit

  SET @I = 1
  SET @InQuote = 0

  SET @XMLStr = ''''''''
  SET @Buf = ''''''''

  WHILE @I < LEN(@JSON + ''''x'''') - 1 BEGIN
    IF @C NOT IN (CHAR(9), CHAR(10), CHAR(13), '''' '''') SET @LastChar = @C

    SET @C = SUBSTRING(@JSON, @I, 1)

    IF @C = ''''"'''' BEGIN
      --Found Quote
      IF @EndedQuote = 1 BEGIN
        --Just exited a quote:  special case for embedded ""
        SET @Buf = @Buf + @C
        SET @InQuote = 1
        SET @EndedQuote = 0
      END
      ELSE IF @InQuote = 1 BEGIN
        --We were already in a quote, so we must be exiting
        SET @InQuote = 0
        SET @EndedQuote = 1
      END
      ELSE BEGIN
        SET @InQuote = 1
      END
    END
    ELSE BEGIN
      --not a quote character

      SET @EndedQuote = 0
      IF (@InQuote = 1) BEGIN
        --just append character
        IF @C NOT IN (CHAR(9), CHAR(10), CHAR(13)) BEGIN
          SET @Buf = @Buf +
            CASE @C
              WHEN ''''<'''' THEN ''''&lt;''''
              WHEN ''''>'''' THEN ''''&gt;''''
              WHEN ''''&'''' THEN ''''&amp;''''
              ELSE @C
            END
        END
      END
      ELSE BEGIN
        --inspect character to determine state

        IF @C = '''':'''' BEGIN
          --@Buf contains VarName
          SET @XMLStr = @XMLStr + ''''<'''' + @Buf + ''''>''''

          INSERT INTO @tvStack (Tag) VALUES (@Buf)

          SET @Buf = ''''''''
        END
        ELSE IF @C = '''','''' BEGIN
          --@Buf contains VarValue
          IF @Buf <> '''''''' BEGIN
            SET @XMLStr = @XMLStr + @Buf
            SET @Buf = ''''''''

            --pop tag from stack and write closing tag to XML
            SET @Tag = ''''''''
            SELECT TOP 1 @Tag = Tag, @StackID = StackID FROM @tvStack ORDER BY StackID DESC
            DELETE FROM @tvStack WHERE StackID = @StackID

            IF @Tag <> '''''''' BEGIN
              SET @XMLStr = @XMLStr + ''''</'''' + @Tag + ''''>''''
            END
          END

          --We are on a comma.  If the top element is an array element, peek and write
          --a close tag and a re-open tag to XML
          SET @IsArrayElem = 0
          SELECT TOP 1 @IsArrayElem = IsArrayElem, @Tag = Tag FROM @tvStack ORDER BY StackID DESC
          IF @LastChar = ''''}'''' AND @IsArrayElem = 1 BEGIN
            SET @XMLStr = @XMLStr + ''''</'''' + @Tag +''''>'''' + ''''<'''' + @Tag + ''''>''''
          END

        END
        ELSE IF @C = ''''['''' BEGIN
          --Start of array.

          --peek at stack and add first array element tag
          SET @Tag = ''''''''
          SELECT TOP 1 @Tag = Tag, @StackID = StackID FROM @tvStack ORDER BY StackID DESC

          IF @Tag <> '''''''' BEGIN
            SET @Tag = @Tag + ''''_''''

            --push array element tag to stack and write closing tag to XML
            INSERT INTO @tvStack (Tag, IsArrayElem) VALUES (@Tag, 1)
            SET @XMLStr = @XMLStr + ''''<'''' + @Tag + ''''>''''
          END
        END
        ELSE IF @C = ''''}'''' BEGIN
          --at end of object

          --pop tag from stack and write closing tag to XML
          SELECT TOP 1 @Tag = Tag, @StackID = StackID FROM @tvStack ORDER BY StackID DESC
          DELETE FROM @tvStack WHERE StackID = @StackID

          IF @Tag <> '''''''' BEGIN
            SET @XMLStr = @XMLStr + @Buf + ''''</'''' + @Tag + ''''>''''
          END
          SET @Buf = ''''''''
        END
        ELSE IF @C = '''']'''' BEGIN
          SELECT TOP 1 @Tag = Tag, @StackID = StackID FROM @tvStack ORDER BY StackID DESC
          DELETE FROM @tvStack WHERE StackID = @StackID

          IF @Tag <> '''''''' BEGIN
            SET @XMLStr = @XMLStr + @Buf + ''''</'''' + @Tag + ''''>''''
          END
          SET @Buf = ''''''''
        END
        ELSE BEGIN
          IF @C NOT IN (CHAR(9), CHAR(10), CHAR(13), ''''{'''') BEGIN
            SET @Buf = @Buf +
              CASE @C
                WHEN ''''<'''' THEN ''''&lt;''''
                WHEN ''''>'''' THEN ''''&gt;''''
                WHEN ''''&'''' THEN ''''&amp;''''
                WHEN '''' '''' THEN ''''''''
                ELSE @C
              END
          END
        END

      END
    END

    SET @I = @I + 1
  END

  --pop any remaining tags from stack
  WHILE EXISTS(SELECT StackID FROM @tvStack) BEGIN
    SET @Tag = ''''''''
    SELECT TOP 1 @Tag = Tag, @StackID = StackID FROM @tvStack ORDER BY StackID DESC
    DELETE FROM @tvStack WHERE StackID = @StackID
    IF @Tag <> '''''''' BEGIN
      SET @XMLStr = @XMLStr + ''''</'''' + @Tag + ''''>''''
    END
  END

  SET @XML = NULLIF(RTRIM(@XMLStr), '''''''')
END
'')

-- ##########################
-- ##
-- ## END OF RUETER''S CODE
-- ##
-- ########################################################

-- ##########################
-- ##
-- ## BEGIN OF NEW UTILS
-- ##
-- ########################################################

if not object_id(''tempdb..#sputilGetTable '') is null
    drop proc #sputilGetTable

exec(''
CREATE PROCEDURE #sputilGetTable
    @name sysname = null,
    @DocID int = null,
    @opt sysname = null,
    @dbg int = null
as
begin
set nocount on
declare @proc sysname,@ret int

select
    @proc=''''#sputilGetTable'''',
    @dbg=isnull(@dbg,0),@docid=isnull(@docid,1)

if @name is null goto help

;WITH DOMTree (
  DEID,
  DocID,
  Tag,
  ID,
  Name,
  Class,
  TextData,
  OpenTagStartPos,
  CloseTagEndPos,
  ParentDEID,
  HUID,
  SortHUID,
  DOMLevel
)
AS
(
SELECT
  dom.DEID,
  dom.DocID,
  dom.Tag,
  dom.ID,
  dom.Name,
  dom.Class,
  dom.TextData,
  dom.OpenTagStartPos,
  dom.CloseTagEndPos,
  dom.ParentDEID,
  CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS HUID,
  CAST(RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
  1 AS DOMLevel
FROM
  #tblDOM dom
WHERE
  -- dom.ParentDEID IS NULL
  dom.DEID=(select DEID from  #tblDOM where tag=''''table'''' and isnull(id,'''''''')=@name)

UNION ALL

SELECT
  dom.DEID,
  dom.DocID,
  dom.Tag,
  dom.ID,
  dom.Name,
  dom.Class,
  dom.TextData,
  dom.OpenTagStartPos,
  dom.CloseTagEndPos,
  dom.ParentDEID,
  CAST(domch.HUID + ''''.'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)) AS varchar(900)) AS HUID,
  CAST(domch.SortHUID + ''''.'''' + RIGHT(''''000000'''' + CAST(ROW_NUMBER() OVER (ORDER BY dom.DEID) AS varchar(900)), 6) AS varchar(900)) AS SortHUID,
  domch.DOMLevel + 1
FROM
  DOMTree domch
  JOIN #tblDOM dom ON
    domch.DEID = dom.ParentDEID
)
--CTE End -----------------------

SELECT *
INTO #xtable
FROM
  DOMTree dom
WHERE
  dom.DocID = @DocID
ORDER BY
  dom.SortHUID

if @dbg=2 select * from #xtable

-- extract table
declare @sql nvarchar(max),@crlf varchar(2)
select @crlf=crlf from fn__sym()

select top 100
    @sql=isnull(@sql+'''','''','''''''')+ -- ''''[''''+textdata+''''] nvarchar(4000)''''+@crlf
    ''''(select top 1 TextData from #xtable ''''+
    '''' where huid like t.huid+''''''''.''''+
    cast((row_number() over (order by huid)) as varchar(5))+
    ''''.%'''''''' and tag is null''''+
    '''') [''''+TextData+'''']''''+@crlf
-- select huid,textdata
from #xtable
where huid like (
    select top 1 huid
    from #xtable
    where tag in(''''tr'''')
    order by sorthuid
    )+''''.%''''
and tag is null
order by sorthuid

-- select @sql=''''create table [''''+@name+''''](''''+@crlf+@sql+'''')''''+@crlf
select @sql=''''select''''+@crlf+@sql +''''from (
    select huid
    from #xtable t
    where t.tag=''''''''tr'''''''' and huid!=''''''''1.1.1''''''''
    ) t
''''

if @dbg=1 exec sp__printsql @sql
exec(@sql)


goto ret

help:
exec sp__usage @proc,''''
Scope
    return content table of html table @name

Notes
    Use #spgetDOM @selector="table" to list tables

Parameters
    @name   name of table
    @DocID  default 1
''''
ret:
return 0
end
'')

if @err!=0 or @dbg>0
    begin
    exec sp__printf ''-- web tools installed''
    exec sp__select_astext ''
    select left(name,charindex(''''_'''',name)-1) web_proc
    from tempdb..sysobjects where name like ''''#sp%''''
    '',@header=1
    end

end -- install option

goto ret

-- =================================================================== errors ==
/*
err_sample1:
exec @ret=sp__err ''code "%s" not exists'',@proc,@p1=@param
goto ret
*/
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    enable a set of #sp to download data from web and explore DOM.

Notes
    this SP encapsulate code of David Rueter, taken from
        http://sourceforge.net/projects/sqldom/files/
    Follow this steps
    1.  open SQLDOM_core_XXX.sql
    2.  replace all single quote with two single quotes
    3.  move examples after last GO below
    4.  put CREATE of #DOM tables into IF not OBJECT_ID(''''...
        and copy tables definitions here
    6.  replaces GO with EXEC(...)
    7.  done

    Tables

        CREATE TABLE #tblDOMDocs(
        DocID int identity PRIMARY KEY,
        DateCreated datetime,
        DocName varchar(128)
        )

        /*
        **************************************************************************************
        TABLE  #tblDOM
        Table #tblDOM  is for internal representation of the DOM data
        **************************************************************************************
        */
        CREATE TABLE #tblDOM (
          DEID int identity PRIMARY KEY,
          DocID int,
          Tag varchar(MAX),
          ID varchar(512),
          Name varchar(512),
          Class varchar(512),
          TextData varchar(MAX),
          OpenTagStartPos int,
          CloseTagEndPos int,
          ParentDEID int
        )

        CREATE INDEX ixDOMTable_ParentDEID ON #tblDOM (ParentDEID) INCLUDE (DEID, DocID)
        CREATE INDEX ixDOMTable_DEID ON #tblDOM (DEID, DocID)

        --NOTE: SQL 2008 introduced filtered indexes, which makes it easy to enforce
        --unqique-but-nullable. If on SQL 2008 or greater AND you wish to enforce uniqueness
        --of ID and Name attributes, uncomment the following two lines
        --  CREATE UNIQUE INDEX tmpixDOMTable_ID ON #tblDOM (ID) INCLUDE (DEID) WHERE ID IS NOT NULL
        --  CREATE UNIQUE INDEX tmpixDOMTable_Name ON #tblDOM (Name) INCLUDE (DEID) WHERE Name IS NOT NULL

        /*
        Note:
        TextData will contain the data for the first text node (if any) under the tag.
        Subsequent text nodes (if any) will be in their own #tblDOM row, with a null TAG
        and referencing the original DEID in the ParentDEID column.
        */


        /*
        **************************************************************************************
        TABLE #DOMAttribs
        Table #tblDOMAttribs is for internal representation of the DOM data--specifically,
        for attributes of DOM elements
        **************************************************************************************
        */
        CREATE TABLE #tblDOMAttribs(
        DOMAttribID int identity PRIMARY KEY,
        DEID int,
        Name varchar(512),
        Value varchar(MAX)
        )

        CREATE UNIQUE INDEX uqDOMAttribs_DEID ON #tblDOMAttribs (DEID, Name)
        CREATE INDEX ixDOMAttribs_DEID ON #tblDOMAttribs (DEID) INCLUDE (Name, Value)

        /*
        **************************************************************************************
        TABLE #tblDOMStyles
        Table #tblDOMAttribs is for internal representation of the DOM data--specifically,
        for attributes of DOM elements
        **************************************************************************************
        */
        CREATE TABLE #tblDOMStyles(
        DOMStyleID int identity PRIMARY KEY,
        DEID int,
        Name varchar(512),
        Value varchar(MAX)
        )

        CREATE UNIQUE INDEX ixDOMStyles_ID ON #tblDOMStyles (DEID, Name)
        CREATE INDEX ixDOMStyles_DEID ON #tblDOMStyles (DEID) INCLUDE (Name, Value)

Parameters
    @opt    options
            install     add web procedures
            uninstall   drop web procedures
            keep        preserve tables otherwise install will drop #dom...
    @dbg    1 show installed #sp

See
    sp__html_table

Examples
    Things to try:

    --Example 1:  Simple parse of string
    EXEC #spactDOMLoad @HTML = ''''<html><body>Hello World.<br /><div><p>SQLDOM <b>ROCKS!</b></p></div></body></html>''''
    EXEC #spgetDOM

    --Example 2:  Render HTML from DOM (that we parsed in Example 1 above)
    EXEC #spgetDOMHTML @PrettyWhitespace=1, @PrintHTML = 1

    --Example 3:  Parse and re-render from a URL
    DECLARE @HTML varchar(MAX)

    EXEC #sputilGetHTTP
      @URL = ''''http://www.google.com'''',
      @ResponseText = @HTML OUTPUT,
      @SuppressResultset = 1

    EXEC #spactDOMLoad @HTML=@HTML
    EXEC #spgetDOM
    EXEC #spgetDOMHTML @PrettyWhitespace=1, @PrintHTML = 1

    --Example 4:  Parse from a string, modify the DOM, render resulting HTML

    EXEC #spactDOMLoad @HTML = ''''<html><body>Hello World.<br /><div id="myContent">Future content goes here</div></body></html>''''

    EXEC #spactDOMLoad @HTML = ''''<div>Here is some neat stuff about <b>SQLDOM</b></div>'''', @Selector = ''''.myContent''''

    EXEC #spgetDOM
    EXEC #spgetDOMHTML @PrettyWhitespace=1, @PrintHTML = 1
''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__web_tool'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__web_tool:

-- ============================================================= sp__web_ws_test
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__web_ws_test',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=121017
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__web_ws_test') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__web_ws_test') with nowait
        goto skip_sp__web_ws_test
        end
    if @ver>121017
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__web_ws_test') with nowait
        goto skip_sp__web_ws_test
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__web_ws_test') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__web_ws_test'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__web_ws_test]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,test
    r:121017\s.zaglio: adapted sp__web_ws to null prev bugs
    r:121016\s.zaglio: bug near N'' and crlf
    r:121015\s.zaglio: test sp__web_ws
    t:sp__web_ws_test @opt=''run'',@dbg=1
*/
CREATE proc sp__web_ws_test
    @tst sysname = null,
    @opt sysname = null,
    @dbg int=0
as
begin
-- set nocount on added to prevent extra result sets from
-- interferring with select statements.
-- and resolve a wrong error when called remotelly
set nocount on
-- @@nestlevel is >1 if called by other sp

declare
    @proc sysname, @err int, @ret int,  -- @ret: 0=OK -1=HELP, any=error id
    -- error vars
    @e_msg nvarchar(4000),              -- message error
    @e_opt nvarchar(4000),              -- error option
    @e_p1  sql_variant,
    @e_p2  sql_variant,
    @e_p3  sql_variant,
    @e_p4  sql_variant

select
    @proc=object_name(@@procid),
    @err=0,
    @ret=0,
    @dbg=isnull(@dbg,0),                -- is the verbosity level
    @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

-- ========================================================= param formal chk ==

-- ============================================================== declaration ==
declare
    -- generic common
    -- @i int,@n int,                      -- index, counter
    -- @sql nvarchar(max),                 -- dynamic sql
    -- options
    -- @opt1 bit,@opt2 bit,
    @crlf nvarchar(2),
    @response nvarchar(max),
    @status nvarchar(4000),
    @url nvarchar(1024),
    @sa  nvarchar(1024),
    @end_declare bit

-- =========================================================== initialization ==
select
    -- @opt1=charindex(''|opt|'',@opt),
    @crlf=crlf,
    @tst=isnull(@tst,''''),
    @end_declare=1
from fn__sym()

-- ======================================================== second params chk ==
if @tst='''' goto help
if @tst=''all'' select @tst=''0''
if isnumeric(@tst)=0 goto err_tst
if @tst<0 or @tst>4 goto err_tst

-- ===================================================================== body ==
-- sp__web_ws_test run,@dbg=1
if @tst in (0,1)
    begin
    exec sp__prints ''simple url get''
    select @url=''http://www.webservicex.com/stockquote.asmx/GetQuote?symbol=MSFT''
    exec @ret=sp__web @url,@rsp=@response out,@sts=@status out,@dbg=@dbg
    if @ret in (0,-176288843) exec sp__printsql @response
    else exec sp__printf ''status=%s'',@status
    end

if @tst in (0,2)
    begin
    exec sp__prints ''not exists test''
    select @url=''http://www.thisdonotexist.exw/i.html''
    exec @ret=sp__web @url,@dbg=@dbg
    exec sp__printf ''@ret:%d'',@ret

    exec sp__prints ''bad method''
    select @url=''http://www.webservicex.com/stockquote.asmx/GetQuote?symbol=MSFT''
    exec @ret=sp__web @url,''got'',@dbg=@dbg
    exec sp__printf ''@ret:%d'',@ret
    end

-- sp__web_ws_test 3,@dbg=1
if @tst in (0,3)
    begin
    exec sp__prints ''web service''
    select @url=''http://www.webservicex.net/globalweather.asmx''
    exec @ret=sp__web @url,
        @rsp=@response out,
        @sa=''http://www.webserviceX.NET/GetWeather'',
        @rcq=''
        <?xml version="1.0" encoding="utf-8"?>
        <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <soap:Body>
            <GetWeather xmlns="http://www.webserviceX.NET">
              <CityName>brescia</CityName>
              <CountryName>italy</CountryName>
            </GetWeather>
          </soap:Body>
        </soap:Envelope>
        '',@sts=@status out,@dbg=@dbg --,@opt=''nodecode''
    if @ret in (0,-176288843) exec sp__printsql @response
    else exec sp__printf ''status=%s'',@status
    end

-- sp__web_ws_test 4,@dbg=1
if @tst in (0,4)
    begin
    exec sp__prints ''smart SOAP call''
    create table #rcqprms(
        id int identity, rid int default(0),
        var nvarchar(500),
        val nvarchar(max)
        )

    insert #rcqprms(var,val) select ''CityName'',''brescia''
    insert #rcqprms(var,val) select ''CountryName'',''italy''

    select @url=''http://www.webserviceX.NET/globalweather.asmx'',
           @sa =''GetWeather''
    exec @ret=sp__web @uri=@url,
                      @sa=@sa,@sts=@status out,@rsp=@response out
                      ,@dbg=@dbg
    if @ret in (0,-176288843) exec sp__printsql @response
    else exec sp__printf ''status=%s'',@status

    drop table #rcqprms
    end -- soap smart call

-- ================================================================== dispose ==
dispose:
-- drop temp tables, flush data, etc.

goto ret

-- =================================================================== errors ==
err:        exec @ret=sp__err @e_msg,@proc,@p1=@e_p1,@p2=@e_p2,@p3=@e_p3,
                              @p4=@e_p4,@opt=@e_opt                     goto ret

err_tst:    select @e_msg=''wrong @tst value''                            goto err
-- ===================================================================== help ==
help:
exec sp__usage @proc,''
Scope
    test the sp__web_ws:
    1. simple test (get a url)
    2. wrong test (bad url)
    3. wrong method (got instead of get)

Parameters
    @tst    id of specific test
            1   test simple get
            2   test wrong params
            3   test SOAP call
            4   test SOAP smart call
            ALL all above

''

select @ret=-1

-- ===================================================================== exit ==
ret:
return @ret

end -- proc sp__web_ws_test'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__web_ws_test:

-- ==================================================================== sp__wiki
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__wiki',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110316
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__wiki') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__wiki') with nowait
        goto skip_sp__wiki
        end
    if @ver>110316
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__wiki') with nowait
        goto skip_sp__wiki
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__wiki') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__wiki'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__wiki]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110316\s.zaglio: a small adapt to new #blob managment
    v:100514\s.zaglio: added code for automanual (see help)
    v:100424\s.zaglio: some small bugs
    v:100405\s.zaglio: added *new+*
    v:100328\s.zaglio: added special *img* tag
    v:100221\s.zaglio: interpret the @txt and write down into #src a coded document
*/
CREATE proc [dbo].[sp__wiki]
    @txt nvarchar(4000)=null,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=''sp__wiki'',@ret=0

if @txt is null goto help

declare
    @buffer nvarchar(4000),@crlf nvarchar(2),@crlf_len int,
    @n int,@i int,@line nvarchar(4000),@p int,@lno int,
    @code bit,@j int,@sum_pos int,@token sysname,
    @lp sysname,@rp sysname,@title sysname,
    @ptr binary(16),@lbegin int,@lend int

if not object_id(@txt) is null
    begin

    create table #blob(id int identity,blob ntext)
    insert #blob(blob) select ''''

    select top 1 @ptr=textptr(blob) from #blob where id=1

    declare cs cursor local for
        select ctext from syscomments where id=object_id(@txt) order by colid
    open cs
    while 1=1
        begin
        fetch next from cs into @line
        if @@fetch_status!=0 break
        updatetext #blob.blob @ptr null null @line
        end
    close cs
    deallocate cs

    exec sp__write_ntext_to_lines @crlf=0 -- split blob into lines, removing end crlf

    select top 1 @lbegin=lno from #src
    where ltrim(rtrim(line)) in (''/*wiki'',''/* wiki'')
    order by lno

    select top 1 @lend  =lno from #src
    where ltrim(rtrim(line)) in (''wiki*/'',''wiki */'')
    order by lno desc

    delete from #src where lno<=@lbegin or lno>=@lend

    drop table #blob

    goto ret
    end -- automanual

create table #summary(lno int, lev int, line nvarchar(4000))
create table #tmp (lno int identity(10,10),line nvarchar(4000))

declare cs cursor local for
    select lno,line from #src order by lno

select @crlf=dbo.fn__crlf(),@crlf_len=len(@crlf)

-- remove 1st crlf
if left(@txt,@crlf_len)=@crlf select @txt=substring(@txt,@crlf_len,4000)

-- adjust crlf (mssql editor fakes)
select @txt=replace(@txt,@crlf,char(13))
select @txt=replace(@txt,char(10),char(13))

if @dbg=1 exec sp__printf ''----------------------------------\n%s'',@txt

if not left(@txt,2) in (''[#'',''[:'')
    begin
    -- split to more lines
    select @n=dbo.fn__str_count(@txt,char(13))
    select @i=1
    while (@i<=@n)
        begin
        select @line=dbo.fn__str_at(@txt,char(13),@i)
        insert #src select rtrim(@line)
        select @i=@i+1
        end
    end
else
    begin
    if @txt in (''[:print:]'',''[:print.text:]'')
        exec sp__print_table ''#src''
    if @txt in (''[:print.html:]'')
        begin

        -- find summary position
        select @sum_pos=lno from #src where line=''[:summary:]''

        -- add end of line
        update #src set line=replace(line,char(13),''<br>''+@crlf)
        if @@error!=0 exec sp__printf ''%s'',''!# error adding end of line''

        -- generate summary
        insert into #summary
        select lno,0,line from #src where lno>@sum_pos and left(line,1)=''+''
        if @@error!=0 exec sp__printf ''%s'',''!# error generating summary(0)''

        update #summary set lev=3,line=substring(line,4,4000) where left(line,3)=''+++''
        if @@error!=0 exec sp__printf ''%s'',''!# error generating summary(1)''
        update #summary set lev=2,line=substring(line,3,4000) where left(line,2)=''++''
        if @@error!=0 exec sp__printf ''%s'',''!# error generating summary(2)''
        update #summary set lev=1,line=substring(line,2,4000) where left(line,1)=''+''
        if @@error!=0 exec sp__printf ''%s'',''!# error generating summary(3)''

        -- add titles
        update #src set line=''<h3><a name="''+substring(line,4,4000)+''">''+substring(line,4,4000)+''</a></h3>'' where left(line,3)=''+++''
        if @@error!=0 exec sp__printf ''%s'',''!# error adding titles (1)''
        update #src set line=''<h2><a name="''+substring(line,3,4000)+''">''+substring(line,3,4000)+''</a></h2>'' where left(line,2)=''++''
        if @@error!=0 exec sp__printf ''%s'',''!# error adding titles (2)''
        update #src set line=''<h1><a name="''+substring(line,2,4000)+''">''+substring(line,2,4000)+''</a></h1>'' where left(line,1)=''+''
        if @@error!=0 exec sp__printf ''%s'',''!# error adding titles (3)''

        -- special replacer
        update #src set line=replace(line,''*new*'',''<img alt="New Icon" height="528" src="images/new-icon.jpg" ''
                                         +''style="width: 30px; height: 20px" title="New Icon" width="592" />''+@crlf)
        if @@error!=0 exec sp__printf ''%s'',''!# error in special replacer(1)''
        update #src set line=replace(line,''*new+*'',''<img alt="New Icon" height="528" src="images/new-icon.jpg" ''
                                         +''style="width: 42px; height: 30px" title="New Icon" width="592" />''+@crlf)
        if @@error!=0 exec sp__printf ''%s'',''!# error in special replacer(2)''

        -- add bold

        select @code=0

        open cs
        while 1=1
            begin
            fetch next from cs into @lno,@line
            if @@fetch_status!=0 break

            /*
            select @i=1,@l=len(@line),@
            while (@i<=@l)
                begin
                select @c=substring(@line,@i,1)
                if @c=''['' select @token=@token+@c
                if @rule=0 and @c=''*'' select @rule=
            */

            select @token=''[:code:''
            select @p=charindex(@token,@line)
            if @p>0
                select @line=left(@line,@p-1)+''<pre class="sh_sql">''
                            +substring(@line,@p+len(@token),4000),
                       @code=1

            -- select @code=@code+dbo.fn__occurrence(@line,''['')
            if @code>0 and left(ltrim(@line),1)='']''
                begin
                select @p=dbo.fn__charindex('']'',@line,-1)
                if @p>0 select @line=left(@line,@p-1)+''</pre>''
                                    +substring(@line,@p+1,4000),
                               @code=0
                /*
                select @p=0
                if @code-dbo.fn__occurrence(@line,'']'')=1
                */
                end

            select @lp=''%*[A-z,0-9]%'',@rp=''%[.,A-z,0-9]*%''
            select @p=patindex(@lp,@line)
            -- print patindex(''%*[A-z,0-9]%'',''*test line'')
            -- print patindex(''%*[A-z,0-9]%'',''* test line'')
            -- print patindex(''%*[A-z,0-9]%'',''test)* line'')
            while (@p>0)
                begin
                select @line=left(@line,@p-1)+''<b>''+substring(@line,@p+1,4000)
                select @p=patindex(@lp,@line)
                end
            -- print patindex(''%[.,A-z,0-9]*%'',''test line.*'')
            select @p=patindex(@rp,@line)
            while (@p>0)
                begin
                select @line=left(@line,@p)+''</b>''+substring(@line,@p+1,4000)
                select @p=patindex(@rp,@line)
                end
            update #src set line=@line where lno=@lno
            if @@error!=0 exec sp__printf ''!# error updating\n%s'',@line
            end -- lines
        close cs

        -- replace summary
        select @n=count(*) from #summary
        if not @sum_pos is null and @n>0
            begin
            truncate table #tmp
            insert #tmp select line from #src
            where lno<@sum_pos order by lno
            insert #tmp select
                replicate(''&nbsp;'',lev)+''<a href="#''+line+''">''+line+''</a>''
            from #summary
            if @@error!=0 exec sp__printf ''%s'',''!# error updating summary (1)''
            insert #tmp select line from #src
            where lno>@sum_pos order by lno
            if @@error!=0 exec sp__printf ''%s'',''!# error updating summary (2)''
            end -- summary
        truncate table #src
        insert #src select line from #tmp order by lno

        -- incapsulate into html and print
        select line from (
        select -100 lno,''<html>'' line union
        select -099 lno,''<head>'' line union
        -- from: http://shjs.sourceforge.net/doc/documentation.html
        -- or: http://softwaremaniacs.org/media/soft/highlight/test.html
        select -098 lno,''<script type="text/javascript" src="hl/js/sh_main.min.js"></script>'' line union
        select -097 lno,''<script type="text/javascript" src="hl/lang/sh_sql.js"></script>'' line union
        select -096 lno,''<link type="text/css" rel="stylesheet" href="hl/css/sh_acid.min.css">'' line union
        select -095 lno,''</head>'' line union
        select -094 lno,''<body onload="sh_highlightDocument();">'' line union
        select -093 lno,''<pre>'' line union
        select lno,line from #src union
        select 1E10 lno,''</pre>'' line union
        select 2E20 lno,''</body></html>'' line
        ) ssrc order by lno
        end -- if @txt in (''[:print.html:]'')
    end -- left(@txt,2) in (''[#'',''[:'')

deallocate cs

goto ret

help:
exec sp__usage @proc,''
Parameters
    @txt    is the text to add to #src

            if exist an object with name @txt, the source will be scanned
            to find a block between "/*wiki" and "wiki*/"

            So to write an sp with automanual:

                create table #src(lno int identity,line nvarchar(4000))
                exec sp__wiki @proc  -- myself
                exec sp__wiki ''''[:print.text]''''
                drop table #src
                /*wiki
                ....
                wiki*/

Wiki rules
    * a crlf keep the paragraph rogether
    * a "."+crlf become a <br>
    * 1st crlf in @txt is removed
    * a "."+space+crlf become a single paragraph
    * [:lng.XXX: ... ]  set syntax highligher for this language
    * [:print:],[:print.text:] compile the doc and print it as text
    * [:print.html:] compile and print with html tags
''

ret:
return @ret
end -- sp__wiki'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__wiki:

-- ===================================================== sp__write_ntext_to_file
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__write_ntext_to_file',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=101109
        begin
        if @aut!='m.stefanoni'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__write_ntext_to_file') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__write_ntext_to_file') with nowait
        goto skip_sp__write_ntext_to_file
        end
    if @ver>101109
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__write_ntext_to_file') with nowait
        goto skip_sp__write_ntext_to_file
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__write_ntext_to_file') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__write_ntext_to_file'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__write_ntext_to_file]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:101109\m.stefanoni: corretto perchè non andava bene il bcp.fmt in c
    v:101105\m.stefanoni: in bcp command line has been added a format file to avoid the 4-byte prefix in generated file
    v:100202\m.stefanoni: removed  "use db_name(); " from bcp parameters for sql 2005 compatibility
    v:091210\s.zaglio: added specific tests
    v:091126\s.zaglio: to ncode
    v:091120\s.zaglio: added @uid/@pwd that if nulls -> trusted connection
    v:091119\s.zaglio: restyle and adde @dbg & error managment & help
    v:090721\m.stefanoni:cleaned up
    v:080807\m.stefanoni:changed to bcp
    v:080806\m.stefanoni:creation
    t:
        sp__write_ntext_to_file ''select top 1 im_file_content
                                 from [srvsin01-it]..rep07_files
                                 where not im_file_content is null'',''%temp%\test.tst'',
                                 @dbg=1
        exec sp__run_cmd ''dir "%temp%\*.tst" & del "%temp%\test.tst"''
*/
CREATE procedure [sp__write_ntext_to_file]
    @sqlfield nvarchar(4000)=null,
    @file nvarchar(400)=null,
    @uid sysname=null, @pwd sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare @ret int,@proc sysname
select @ret=0,@proc=''sp__write_ntext_to_file''

if @sqlfield is null or @file is null or charindex(''*'', @file)>0 or charindex(''?'', @file) > 0
    goto help

select @sqlfield=replace(@sqlfield,char(13),'''')
select @sqlfield=replace(@sqlfield,char(10),'''')
declare @cmd nvarchar(4000) -- generica per la xp_cmdshell


declare @format_file nvarchar(400)
DECLARE @temp_dir nvarchar(256)
EXEC sp__get_temp_dir @temp_dir OUT


SET @cmd = ''echo 9.0 > "'' + @temp_dir + ''\bcp.fmt"''
exec master..xp_cmdshell @cmd, NO_OUTPUT

SET @cmd = ''echo 1 >> "'' + @temp_dir + ''\bcp.fmt"''
exec master..xp_cmdshell @cmd, NO_OUTPUT

SET @cmd = ''echo 1 SQLBINARY 0 0 "" 1 data "" >> "'' + @temp_dir + ''\bcp.fmt"''
exec master..xp_cmdshell @cmd, NO_OUTPUT


-- sp__run_cmd ''bcp''
-- print dbo.fn__servername(null)
set @cmd = ''@bcp "'' + @sqlfield + ''" queryout "'' + @file + ''" -S '' + dbo.fn__servername(null) + '' -F 1 -L 1 -n -f "'' + @temp_dir + ''\bcp.fmt" ''
if @uid is null and @pwd is null select @cmd=@cmd+''-T ''
else select @cmd=@cmd+''-U ''+@uid+'' -P ''+coalesce(@pwd,'''')+'' ''

if @dbg=1 exec sp__printf @cmd

create table #src (lno int identity(10,10),line nvarchar(4000))
insert into #src(line)
exec @ret=master..xp_cmdshell @cmd

if @ret=0 and exists(select null from #src where line like ''Error = %'') select @ret=1
if @dbg=1 exec sp__print_table ''#src''
drop table #src

return @ret

help:
select @cmd =''Usage:\n''
            +''\t@sqlfield is the sql code that return a text/image field\n''
            +''\t@file     is the name of destination file''
            +''\t@file cannot contain wild card''
exec sp__usage @proc,@cmd
return -1
end -- proc'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__write_ntext_to_file:

-- ==================================================== sp__write_ntext_to_lines
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__write_ntext_to_lines',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=110509
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__write_ntext_to_lines') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__write_ntext_to_lines') with nowait
        goto skip_sp__write_ntext_to_lines
        end
    if @ver>110509
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__write_ntext_to_lines') with nowait
        goto skip_sp__write_ntext_to_lines
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__write_ntext_to_lines') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__write_ntext_to_lines'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__write_ntext_to_lines]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility
    v:110509\s.zaglio: a bug near last line without crlf
    v:110329\s.zaglio: added @nosrc
    v:110316\s.zaglio: added multi blob management
    v:100919\s.zaglio: managed empty #blob
    v:100515\s.zaglio: added @crlf to remove line terminator
    v:100514\s.zaglio: rewritten
    t:
        create table #src(lno int identity(10,10),line nvarchar(4000))
        exec sp__write_ntext_to_lines ''select blob from gamon.ramses.dbo.SETUP0H_WIKIDOCS where id=971''
        exec sp__print_table ''#src''
        drop table #src
*/
CREATE proc [dbo].[sp__write_ntext_to_lines]
    @sqlfield nvarchar(4000)=null,
    @crlf bit=1,
    @nosrc bit=0,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0
if @sqlfield is null and object_id(''tempdb..#blob'') is null goto help

declare
    @drop bit,
    @n int,@i int,@p int,@j int,@id int,
    @ncrlf nvarchar(2),@cr nchar(1),@lf nchar(1),
    @lcrlf int

declare @lines table (pos int primary key,leng int)

select @dbg=isnull(@dbg,0),@crlf=isnull(@crlf,1),@nosrc=isnull(@nosrc,0)

if object_id(''tempdb..#blob'') is null
    begin
    create table #blob(blob ntext)
    exec(''insert #blob ''+@sqlfield)
    select @drop=1
    end
else
    select @drop=0

select @ncrlf=crlf,@cr=cr,@lf=lf,@lcrlf=len(crlf)
from dbo.fn__sym()

declare cs cursor local for
    select id
    from #blob
    where 1=1
open cs
while 1=1
    begin
    fetch next from cs into @id
    if @@fetch_status!=0 break

    delete from @lines

    -- identify row separator
    select top 1 @i=charindex(@cr,blob),@j=charindex(@lf,blob)
    from #blob b
    where b.id=@id

    if @i is null begin select @ret=-2 goto ret end

    if @i>0 and @j=0 select @ncrlf=@cr
    if @i=0 and @j>0 select @ncrlf=@lf
    if @i=@j+1 select @ncrlf=@lf+@cr
    -- else is @ncrlf

    select top 1 @i=1,@p=1,@j=1,@n=datalength(blob)/2
    from #blob where id=@id

    if @dbg=1 exec sp__printf ''i=%d,n=%d,id=%d'',@i,@n,@id

    while 1=1
        begin
        select top 1 @i=charindex(@ncrlf,substring(blob,@j,4000))
        from #blob b
        where b.id=@id

        if @i=0
            begin
            -- last pieces that do not end with crlf
            -- if @dbg=1 exec sp__printf ''j:%d, i:%d, @n:%d'',@j,@i,@n
            if @n>@j/*+@lcrlf*/-1 insert @lines select @j,@n-@j+1
            break
            end
        -- if @dbg=1 exec sp__printf ''j:%d, i:%d'',@j,@i
        if @crlf=1 or @i-@lcrlf+1<0
            insert @lines select @j,@i
        else
            insert @lines select @j,@i-@lcrlf+1
        select @j=@j+@i+@lcrlf-1
        end -- while

    if @dbg=1
        begin
        select pos,leng,substring(blob,pos,leng) line
        from #blob b,@lines
        where b.id=@id
        order by pos desc
        end
    else
        begin
        if object_id(''tempdb..#src'') is null or @nosrc=1
            select substring(blob,pos,leng) line
            from #blob b,@lines
            where b.id=@id
            order by pos
        else
            insert into #src
            select substring(blob,pos,leng) line
            from #blob b,@lines
            where b.id=@id
            order by pos
        end
    end -- while of cursor
close cs
deallocate cs


if @drop=1 drop table #blob

goto ret

help:
exec sp__usage @proc,''
Scope
    explode blobs into lines

Params:
    @sqlfiled must be a query that return a text/ntext field
              or can be null but must exists #blob
    @crlf     (default 1) leave line terminator; 0 remove it
    @nosrc    ignore #src and out to stdout to insert into @src etc.

    create table #blob(id int identity,blob ntext)
    create table #src(lno int identity(10,10),line nvarchar(4000))
''
select @ret=-1

ret:
return @ret
end -- proc sp__write_ntext_to_lines'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__write_ntext_to_lines:

-- ============================================================== sp__xls_attach
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__xls_attach',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=130906
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__xls_attach') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__xls_attach') with nowait
        goto skip_sp__xls_attach
        end
    if @ver>130906
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__xls_attach') with nowait
        goto skip_sp__xls_attach
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__xls_attach') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__xls_attach'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__xls_attach]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,xls
    v:130906\s.zaglio: adapted to fn__parse_url rename
    v:130217\s.zaglio: added resources into help and q option
    r:120314\s.zaglio: adapted to sql2k5 (32&64bit)
    v:100508\s.zaglio: removed convertion, used imex
    v:100501\s.zaglio: skip print/area/filters..and not real sheets; added imex and @nohd
    v:100424\s.zaglio: create views for xls files
    t:sp__xls_attach ''c:\shared_folders\backup_db\'',''xls_tests'' ,@dbg=1
    t:sp__dir ''xls*'' -- sp__drop ''xls_tests_*'',@simul=0
*/
CREATE proc [dbo].[sp__xls_attach]
    @path nvarchar(512)=null,
    @root sysname=null,
    @opt sysname=null,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=object_name(@@procid),@ret=0,
       @opt=dbo.fn__str_quote(isnull(@opt,''''),''|'')

if @path is null goto help

-- ================================================================== declare ==
declare
    @cmd nvarchar(1024), @file nvarchar(512),
    @tbl sysname,@i int,@n bigint,
    @sql nvarchar(4000),@view sysname,
    @flds nvarchar(4000),@psep nchar(1),
    @crlf nvarchar(2),@tmp sysname,@obj sysname,
    @quiet bit
create table #xls(id int identity,[file] nvarchar(4000))
create table #tbls(
    id int identity,
    table_cat sysname null,
    table_schem sysname null,
    table_name sysname null,
    table_type sysname null,
    remarks sysname null,
    view_name sysname null
    )
create table #providers(
    cod sysname,
    parse sysname,
    des sysname
    )

-- ===================================================================== init ==
select
    @quiet=charindex(''|q|'',@opt)|charindex(''|quiet|'',@opt),
    @psep=psep,
    @crlf=crlf
from dbo.fn__sym()

if not right(@path,4)=''.xls''
and not right(@path,5)=''.xlsx''
    begin
    if right(@path,1)!=''\'' select @path=@path+''\''
    select @path=@path+''*.xls?''
    end

-- xp_cmdshell ''dir /b "d:\sapshare\xls_seltris\*.xls"''
select @cmd=''dir /s /b "''+@path+''"''

-- drop table #stdout

-- ===================================================================== body ==

-- check providers
insert #providers exec xp_enum_oledb_providers
if not exists(
    select cod from #providers
    where cod in (''MSDASQL'',''Microsoft.ACE.OLEDB.12.0'')
    )
    goto err_prv
-- OLEDB Provider for ODBC (MSDASQL)

if @dbg=1 exec sp__printf''path=%s'',@path

insert #xls
exec xp_cmdshell @cmd
delete from #xls where [file] is null -- last null row

select @n=max(id) from #xls
if @n is null goto err_nof

declare cs cursor local for
    select [file]
    from #xls
    where 1=1
open cs
while 1=1
    begin
    fetch next from cs into @file
    if @@fetch_status!=0 break

    if @quiet=0 exec sp__printf ''-- link file %s'',@file

    -- some sheet shave wrong cells other not work
    if exists(select null from master..sysservers where srvname=''$xls'')
        exec master.dbo.sp_dropserver @server=N''$xls'', @droplogins=''droplogins''

    -- declare @file sysname,@tmp sysname select @file=''c:\shared_folders\backup_db\sqltest.xlsx''
    select @tmp=''xls_''+replace(convert(sysname,newid()),''-'',''_'')
    exec sp_addlinkedserver
        @server = @tmp,
        @srvproduct=''xls'',
        @provider=''microsoft.ace.oledb.12.0'',
        @datasrc=@file,
        @provstr=''excel 12.0;readonly=1''

    -- sp__xls_attach ''c:\backup''
    declare @su sysname select @su=system_user
    exec sp_addlinkedsrvlogin @tmp, ''false'', @su, N''ADMIN'', NULL

    truncate table #tbls

    insert #tbls(table_cat,table_schem,table_name,table_type,remarks)
    exec master.dbo.sp_tables_ex @table_server = @tmp

    -- select * from #tbls

    if @dbg=0 exec master.dbo.sp_dropserver @server=@tmp, @droplogins=''droplogins''

    if @dbg=1 exec sp__select_astext ''#tbls'',@header=1

    -- declare @file sysname,@root sysname select @file=''c:\shared_folders\backup_db\sqltest.xlsx''

    if @root is null
        update #tbls set
            view_name=''xls_''+replace(dbo.fn__format(@file,''AN'',default),''__'',''_'')+
                      ''_''+table_name
        from #tbls
    else
        begin
        select
            @root+''_''+dbo.fn__format(page,''AN'',default)+''_''+table_name
        from #tbls,dbo.fn__parse_url(''file:///''+@file,default)

        update #tbls set
            view_name=@root+''_''+dbo.fn__format(page,''AN'',default)+''_''+table_name
        from #tbls,dbo.fn__parse_url(''file:///''+@file,default)
        end

    -- strip last dollar
    update #tbls set
        view_name=left(view_name,len(view_name)-1)
    where right(view_name,1)=''$''

    -- create view for sheets with data

    declare sh cursor local for
        select table_name,quotename(view_name)
        from #tbls
        where right(table_name,1)=''$'' -- skip subsheets/view/filters/print areas...
    open sh
    while 1=1
        begin
        fetch next from sh into @tmp,@obj
        if @@fetch_status!=0 break

        select @sql=''select @n=count(*) from openrowset(''''MSDASQL'''',''+
        ''''''DRIVER=Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb);''+
        ''UID=admin;UserCommitSync=Yes;Threads=3;SafeTransactions=0;ReadOnly=1;''+
        ''PageTimeout=5;MaxScanRows=8;MaxBufferSize=2048;FIL=excel 14.0;DriverId=1046;''+
        ''DBQ=''+@file+'''''',''''''+
        '' select top 1 * from [''+@file+''].[''+@tmp+'']'''')''

        select @n=null
        if @dbg=1 exec sp__printsql @sql
        exec sp_executesql @sql,N''@n bigint out'',@n=@n out
        if isnull(@n,0)>0
            begin
            if not object_id(@obj) is null exec(''drop view ''+@obj)
            select @sql=
                ''create view ''+@obj+'' as ''+
                ''select * from openrowset(''''MSDASQL'''',''+
                ''''''DRIVER=Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb);''+
                ''UID=admin;UserCommitSync=Yes;Threads=3;SafeTransactions=0;ReadOnly=1;''+
                ''PageTimeout=5;MaxScanRows=8;MaxBufferSize=2048;FIL=excel 14.0;DriverId=1046;''+
                ''DBQ=''+@file+'''''',''''''+
                '' select * from [''+@file+''].[''+@tmp+'']'''')''
            exec(@sql)
            end
        end -- while of cursor sheets
    close sh
    deallocate sh

    end -- while of cursor files
close cs
deallocate cs

dispose:
drop table #providers
drop table #xls
drop table #tbls
goto ret

-- =================================================================== errors ==
err_nof:    exec @ret=sp__err ''no xls files found'',@proc
            goto ret
err_prv:    exec @ret=sp__err ''"MSDASQL" and "Microsoft.ACE.OLEDB" providers are required'',@proc
            goto ret

-- ===================================================================== help ==

help:
exec sp__usage @proc,''
Scope
    Will search all XLS files under @path and create a xls_* view for each sheet

Parameters
    @path   is the path where search
    @root   is the root name for tview as root_file_sheet
            otherwise will be used xls_path_file_sheet
    @opt    options
            q|quiet     do not show messages

Notes
    * the new version uses only MSDASQL provider that require to install
      the MSAccess runtime.
    * if the file do not exists anymore, the select on view return
      a wrong message about gesitration of OLEDB that can be confused
      with previos point
    * can be necessary run this to exable linked server
        use msdb
        go
        sp_configure ''''show advanced options'''', 1
        go
        reconfigure with override
        go
        sp_configure ''''ad hoc distributed queries'''', 1
        go
        reconfigure with override
        go
    * resources:
      http://www.ashishblog.com/blog/importexport-excel-xlsx-or-xls-file-into-sql-server/

Examples
    sp__xls_attach "c:\shared_folders\backup_db\","xls_tests",@dbg=1
''

-- ===================================================================== exit ==

ret:
return @ret
end -- sp__xls_attach'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__xls_attach:

-- ============================================================= sp__xls_convert
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__xls_convert',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100424
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__xls_convert') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__xls_convert') with nowait
        goto skip_sp__xls_convert
        end
    if @ver>100424
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__xls_convert') with nowait
        goto skip_sp__xls_convert
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__xls_convert') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__xls_convert'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__xls_convert]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,xls
    v:100424\s.zaglio: create aid code to import data from xls
    t:sp__xls_convert ''anagrafica''
*/
CREATE proc sp__xls_convert @sheet sysname=null
as
begin
set nocount on
select @sheet=''%''+coalesce(@sheet+''%'','''')
declare @crlf nchar(2)
select @crlf=crlf from dbo.fn__sym()
create table #src (lno int identity, line nvarchar(4000))
insert into #src
select name from sysobjects where name like ''xls[_]''+@sheet

update #src set
    line=''select ''+dbo.fn__str_exp(@crlf+
            ''    [%%] = convert(nvarchar(4000),nullif(ltrim(rtrim([%%])),''''''''))'',
            dbo.fn__flds_of(line,'','',null),'','')+@crlf
        +''into #''+line + @crlf
        +''from ''+quotename(line) + @crlf + @crlf

exec sp__print_table ''#src''

drop table #src
end'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__xls_convert:

-- ============================================================== sp__xls_create
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__xls_create',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100508
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__xls_create') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__xls_create') with nowait
        goto skip_sp__xls_create
        end
    if @ver>100508
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__xls_create') with nowait
        goto skip_sp__xls_create
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__xls_create') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__xls_create'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__xls_create]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,xls
    v:100508\s.zaglio: cerate an xls files or add a sheet to an existins
    t:sp__xls_create ''test_form'',''c:\backup\test_form.xls'',@or=1
*/
CREATE procedure [dbo].[sp__xls_create]
    @tbl sysname,
    @xls nvarchar(512),
    @or bit=0,
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=''sp__xls_create'',@ret=0

declare
    @sql nvarchar(4000)

if @or=1 exec sp__drop @xls,@simul=0

select @sql=null
select @sql=coalesce(@sql+'','','''')+quotename(c.name)+'' text'' from syscolumns c where id=object_id(@tbl)
select @sql=''create table [''+@tbl+''](''+@sql+'')''

exec sp__xls_sql @xls,@sql,@dbg=@dbg
goto ret

help:
exec sp__usage @proc,''

Parameters
    @or     if 1 delete existing file
''

ret:
return @ret
end -- sp__xls_create'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__xls_create:

-- ================================================================= sp__xls_sql
select @ver=null,@aut=null
exec sp_executesql N'
    select
        @ver=cast(val1 as decimal(10,4)),
        @aut=val2
    from dbo.fn__script_info(@obj,@typ,0)
    ',N'@obj sysname,@typ char(2),@ver numeric(10,4) out,@aut sysname out',
    @obj='sp__xls_sql',@typ='rv',@ver=@ver out,@aut=@aut out
if not @ver is null
    begin
    if @ver=100508
        begin
        if @aut!='s.zaglio'
            raiserror('local "%s.%s" with same version but different author',
                      16,1,@db,'sp__xls_sql') with nowait
        raiserror('skipped "%s.%s" because local is the same',
                  10,1,@db,'sp__xls_sql') with nowait
        goto skip_sp__xls_sql
        end
    if @ver>100508
        begin
        raiserror('skipped "%s.%s" because local is more recent',
                  10,1,@db,'sp__xls_sql') with nowait
        goto skip_sp__xls_sql
        end
    end

raiserror('re-creating "%s.%s"',10,1,@db,'sp__xls_sql') with nowait

if exists(
    select top 1 null from sys.objects
    where name='sp__xls_sql'

    and schema_id=schema_id('dbo')
    )
    drop proc [dbo].[sp__xls_sql]

begin try
exec dbo.sp_executesql @statement = N'/*  leave this
    l:see LICENSE file
    g:utility,xls
    todo: rearrange code for a better error management
    v:100508\s.zaglio: execute a sql command into the xls sheet
    c:see http://www.simple-talk.com/sql/t-sql-programming/sql-server-excel-workbench/
    t:sp__xls_sql ''c:\backup\test_form.xls'',''insert into test_form(id,i_val,f_val,d_val,v_val,t_val) select 1,2,3,4,5,6''
    t:sp__xls_sql ''c:\backup\test_form.xls'',''delete from test_form'' -- delete not supported
    t:sp__xls_sql ''c:\backup\test_form.xls'',''update test_form set id=null where id=''''0''''''
    t:
        create trigger tr_xls_test_form_test_form
            on xls_test_form_test_form
            instead of insert,update,delete
        as
        begin
        exec sp__xls_sql ''c:\backup\test_form.xls'',''insert into test_form(id,i_val,f_val,d_val,v_val,t_val) select 1,2,3,4,5,6''
        end

        insert into xls_test_form_test_form select 1,2,3,4,5,6

        drop trigger tr_xls_test_form_test_form
*/
CREATE procedure [dbo].[sp__xls_sql]
    @xls nvarchar(512),
    @sql nvarchar(4000),
    @dbg bit=0
as
begin
set nocount on
declare @proc sysname,@ret int
select @proc=''sp__xls_sql'',@ret=0

declare
    @objexcel int,
    @hr int,
    @command sysname,
    @strerrormessage sysname,
    @objerrorobject int,
    @objconnection int,
    @bucket int,
    @worksheet sysname,
    @connectionstring nvarchar(1024),
    @crlf nchar(2)

select @crlf=crlf from dbo.fn__sym()

select @connectionstring =
    ''provider=microsoft.jet.oledb.4.0;data source=%ds%;extended properties=excel 8.0''

select @connectionstring=replace (@connectionstring, ''%ds%'', @xls)

select @strerrormessage=''making adodb connection '',
            @objerrorobject=null
exec @hr=sp_oacreate ''adodb.connection'', @objconnection out
if @hr=0
    select @strerrormessage=''assigning connectionstring property "''
            + @connectionstring + ''"'',
            @objerrorobject=@objconnection
if @hr=0 exec @hr=sp_oasetproperty @objconnection,
            ''connectionstring'', @connectionstring
if @hr=0 select @strerrormessage
        =''opening connection to xls, for file create or append''
if @hr=0 exec @hr=sp_oamethod @objconnection, ''open''

-- todo: convert @tbl in create with sp__script ....
-- declare @tbl sysname,@sql nvarchar(4000),@crlf nchar(2) select @tbl=''test_form'',@crlf=char(13)+char(10)

if @hr=0 select @strerrormessage
        =''executing ddl "''+@sql+''"''
if @dbg=1 exec sp__printf ''%s'',@sql

-- first we have to delete existing
if @hr=0 exec @hr=sp_oamethod @objconnection, ''execute'',
                  @bucket out , @sql
if @hr<>0
    begin
    declare
        @source varchar(255),
        @description varchar(255),
        @helpfile varchar(255),
        @helpid int

    execute sp_oageterrorinfo @objerrorobject, @source output,
        @description output,@helpfile output,@helpid output
    select @strerrormessage=''error whilst ''
        +coalesce(@strerrormessage,''doing something'')+'', ''
        +coalesce(@description,'''')
    raiserror (@strerrormessage,16,1)
    end

goto ret

help:
exec sp__usage @proc

ret:
if @objconnection!=0 exec @hr=sp_oadestroy @objconnection
return @ret
end -- sp__xls_create'

end try
begin catch
exec #script_catch 'utility',@repeat
if not error_number() in (208,207)
    begin
    select @emsg=error_message(),@esev=error_severity(),@ests=error_state()
    raiserror(@emsg,@esev,@ests)
    end
end catch
skip_sp__xls_sql:


-- ##########################
-- ##
-- ## re-install/upgrade script tracer
-- ## upgrade LOG_DDL if necessary
-- ##
-- ########################################################
exec sp__script_trace_db 'install'

dispose:
/*  to manage dependencies we run eventually the script 2 or 3 times */
if exists(
    select top 1 null
    from #script_results
    where number in (208,207)  -- invalid object, invalid column name
    and rep=@repeat
    -- a difference between x32 and x64 of mssql 2k5/8
    and not message like '%''cpu_ticks_in_ms''%'
    )
    begin
    select @repeat=@repeat+1
    select @msg=replicate('#',80)+' REPEAT '+cast(@repeat as char)
    raiserror(@msg,10,1)
    if @repeat<3
        goto begin_of_script
    else
        begin
        select db_name() db,* from #script_results
        raiserror('script repeats failure',16,1)
        end
    end

exec sp__utility_setup @opt='run|end'

-- select * from #script_results
-- drop table #script_results

end_of_script:

-- finished:17-12-14T16:14
