SQL Server-Agent-Aufträge schlagen fehl, wenn die Aufträge Auftragsschritte enthalten, die nach der Installation von SQL Server 2005 Service Pack 1 Token verwenden


Fehler: 426808 (SQLBUDT)

Problembeschreibung


Nachdem Sie Microsoft SQL Server 2005 Service Pack 1 (SP1) installiert haben, wird das folgende Verhalten auftreten:
  • Aufträge des SQL Server-Agents schlagen fehl, wenn die Aufträge Auftragsschritte enthalten, die Token verwenden.
  • Die folgende Fehlermeldung wird angezeigt:
    Der Auftragsschritt enthält mindestens ein Token. Für SQL Server 2005 Service Pack 1 oder höhere Versionen müssen alle Auftragsschritte mit Token mit einem Makro aktualisiert werden, bevor der Auftrag ausgeführt werden kann.
Hinweis Dieses Problem tritt bei Build 2046 oder höheren Versionen von SQL Server 2005 auf.

Ursache


In SQL Server 2005 SP1 hat sich die Syntax des Auftragsschritt Tokens des SQL Server-Agents geändert. Sie müssen nun ein Escape-Makro mit allen Tokens einbeziehen, die in Auftragsschritten verwendet werden. Wenn Sie kein Escape-Makro einfügen, treten bei diesen Auftragsschritten Fehler auf. In der folgenden Tabelle sind die Escape-Makros aufgeführt.
Escape-MakroBeschreibung
$(ESCAPE_SQUOTE(TokenName))Dieses Makro maskiert Apostrophe (') in der Zeichenfolge des Tokens. Das Makro ersetzt ein Apostroph durch zwei Apostrophe.
$(ESCAPE_DQUOTE(TokenName))Dieses Makro maskiert Anführungszeichen (") in der Token-Ersatzzeichenfolge. Das Makro ersetzt ein Anführungszeichen durch zwei Anführungszeichen.
$(ESCAPE_RBRACKET(TokenName))Dieses Makro maskiert rechte eckige Klammern (]) in der Token-Ersetzungszeichenfolge. Das Makro ersetzt eine rechte eckige Klammer durch zwei rechte eckige Klammern.
$ (ESCAPE_NONE (Tokenname))Das Makro ersetzt ein Token, ohne dass Zeichen in der Zeichenfolge zu entkommen sind. Dieses Makro wird zur Unterstützung der Abwärtskompatibilität in Umgebungen bereitgestellt, in denen Token-Ersetzungszeichenfolgen nur von vertrauenswürdigen Benutzern erwartet werden.
Ein Auftragsschritt kann beispielsweise die folgende Transact-SQL-Anweisung enthalten, die das DBN- Token verwendet:
CREATE DATABASE [$(A-DBN)]
In diesem Beispiel müssen Sie die Token-Syntax auf die folgende Syntax aktualisieren:
CREATE DATABASE [$(ESCAPE_RBRACKET(A-DBN))]
Diese Änderung unterscheidet sich vom vorherigen Verhalten von SQL Server 2005, bei dem keine Escape-Makros benötigt wurden.

Fehlerbehebung


Um dieses Problem zu beheben, aktualisieren Sie entweder alle oder nur bestimmte Aufträge, die Token für die neue Token-Syntax verwenden. Verwenden Sie dazu die gespeicherte Prozedur sp_AddEscapeNoneToJobStepTokens . Sie können diese gespeicherte Prozedur mit dem folgenden Transact-SQL-Skript erstellen.Hinweis Stellen Sie sicher, dass der SQL Server 2005 SP1-Build, den Sie installieren, Build 2046 oder höher ist. Darüber hinaus müssen Sie Mitglied der festen Serverrolle sysadmin sein, um das Skript ausführen zu können.
      -- This script is used to automatically edit SQL Agent job steps so that-- unescaped tokens are prefaced with the ESCAPE_NONE syntax that was added in-- SQL Server 2005 SP1.if (@@microsoftversion < 0x90007FE)BEGIN    RAISERROR('This script should only be run on at least SQL Server 2005 SP1.', 20, 127) WITH LOG    returnENDuse msdbgoif exists (select * from sys.objects where name = N'fn_PrefaceTokensWithEscapeNone' and type = 'FN')    drop function fn_PrefaceTokensWithEscapeNonego-- This function manipulates @commands so that all bare tokens-- are prefaced with ESCAPE_NONE.create function fn_PrefaceTokensWithEscapeNone(@commands nvarchar(max)) RETURNS nvarchar(max)ASBEGIN    if (@commands IS NULL)    BEGIN        return @commands    END    -- In order to let this script run under SQLCMD mode, we define    -- the special "$(" variable start string by concatenation so that    -- sqlcmd mode does not think that we are defining one of its variables.    declare @strVariableStart nchar(2)    select @strVariableStart = N'$' + N'('    declare @idxTokenStart int    select @idxTokenStart = CHARINDEX(@strVariableStart, @commands)    while (@idxTokenStart != 0 and @idxTokenStart is not null)    BEGIN        declare @idxCloseParen int        select @idxCloseParen = CHARINDEX(N')', SUBSTRING(@commands, @idxTokenStart, LEN(@commands)))        -- Error checking. If there is no close parenthesis, return.        if (0 = @idxCloseParen)        BEGIN            return @commands        END        -- Deduce the token variable.        declare @tokenLen int        select @tokenLen = @idxCloseParen - LEN(@strVariableStart) - 1        declare @token nvarchar(max)        select @token = SUBSTRING(@commands, @idxTokenStart + LEN(@strVariableStart), @tokenLen)        -- Verify if @token contains a mis-matched number of open and        -- close parens. This behavior could happen if invalid syntax is        -- in a comment block. If so, skip to the next token.        declare @idx int        declare @cOpenParens int        declare @cCloseParens int        select @cOpenParens = 0        select @idx = CHARINDEX(N'(', @token);        while (@idx != 0)        BEGIN            select @cOpenParens = @cOpenParens + 1            select @idx = CHARINDEX(N'(', @token, @idx + 1);        END        select @cCloseParens = 0        select @idx = CHARINDEX(N')', @token);        while (@idx != 0)        BEGIN            select @cCloseParens = @cCloseParens + 1            select @idx = CHARINDEX(N')', @token, @idx + 1);        END        -- Special case for the WMI token.        if (N'WMI(' = SUBSTRING(@token, 1, LEN(N'WMI(')))        BEGIN                select @cOpenParens = @cOpenParens - 1        END        if ((@cOpenParens = @cCloseParens) and            (N'ESCAPE_NONE(' != SUBSTRING(@token, 1, LEN(N'ESCAPE_NONE('))) and            (N'ESCAPE_SQUOTE(' != SUBSTRING(@token, 1, LEN(N'ESCAPE_SQUOTE('))) and            (N'ESCAPE_DQUOTE(' != SUBSTRING(@token, 1, LEN(N'ESCAPE_DQUOTE('))) and            (N'ESCAPE_RBRACKET(' != SUBSTRING(@token, 1, LEN(N'ESCAPE_RBRACKET('))))        BEGIN            select @commands = STUFF(@commands, @idxTokenStart + LEN(@strVariableStart), @tokenLen, N'ESCAPE_NONE(' + @token + N')')        END        select @idxTokenStart = CHARINDEX(@strVariableStart, @commands, @idxTokenStart + 1)    END    return @commandsENDgoif exists (select * from sys.objects where name = N'sp_AddEscapeNoneToJobStepTokens' and type = 'P')    drop procedure sp_AddEscapeNoneToJobStepTokensgo-- This procedure allows you to update jobs so that bare tokens-- are prefaced with ESCAPE_NONE. By default, all jobs are updated.-- You can optionally specify @job_name, @job_id, or @owner_name-- to limit the jobs that will be affected.CREATE PROCEDURE sp_AddEscapeNoneToJobStepTokens(    @job_name nvarchar(128) = null,    @job_id uniqueidentifier = null,    @owner_name nvarchar(256) = null)AS  -- Find the jobs to update. These jobs must match all of the input  -- criteria, unless all of the inputs are null. In this case,   -- examine all jobs.  The jobs must also be jobs created locally,  -- such as sysjobs.originating_server_id = 0. These jobs should not be a job that we run  -- because another server told us to.  Furthermore, if the job  -- is local but it is meant to be run on a target server, we send an  -- update for the job.  declare @jobsToUpdate TABLE (job_id uniqueidentifier not null)    insert into @jobsToUpdate      select job_id      from sysjobs      where originating_server_id = 0 -- local jobs      and ((COALESCE(@job_name, sysjobs.name) = sysjobs.name) and           (COALESCE(@job_id, sysjobs.job_id) = sysjobs.job_id) and           (COALESCE(@owner_name, suser_sname(sysjobs.owner_sid)) = suser_sname(sysjobs.owner_sid)))    -- Now find the job steps to update, creating the new command by using  -- fn_PrefaceTokensWithEscapeNone.  declare @jobStepsToUpdate TABLE (job_id uniqueidentifier not null,                                    step_id int not null,                                    command_old nvarchar(max) null,                                    command_new nvarchar(max) null,                                   output_file_old nvarchar(max) null,                                    output_file_new nvarchar(max) null)    insert into @jobStepsToUpdate  (job_id, step_id, command_old, command_new, output_file_old, output_file_new)      select job_id, step_id, command, dbo.fn_PrefaceTokensWithEscapeNone(command), output_file_name, dbo.fn_PrefaceTokensWithEscapeNone(output_file_name)      from sysjobsteps      where sysjobsteps.job_id =       (select job_id        from @jobsToUpdate        where job_id = sysjobsteps.job_id)    -- Now we update the actual job step commands. We do this first before  -- we push out the updated jobs to the target servers so the  -- target servers actually get the updated version.  declare @updated_job_id uniqueidentifier  declare @updated_job_step_id int  declare @updated_job_step_command nvarchar(max)  declare @updated_job_step_output_file nvarchar(max)    declare job_steps_cursor CURSOR FOR      select job_id, step_id, command_new, output_file_new      from @jobStepsToUpdate      order by job_id, step_id    OPEN job_steps_cursor  FETCH NEXT from job_steps_cursor into @updated_job_id, @updated_job_step_id, @updated_job_step_command, @updated_job_step_output_file  WHILE (@@FETCH_STATUS <> -1)  BEGIN      IF (@@FETCH_STATUS <> -2)      BEGIN          EXEC sp_update_jobstep @job_id = @updated_job_id, @step_id = @updated_job_step_id, @command = @updated_job_step_command, @output_file_name = @updated_job_step_output_file      END      FETCH NEXT from job_steps_cursor into @updated_job_id, @updated_job_step_id, @updated_job_step_command, @updated_job_step_output_file  END    CLOSE job_steps_cursor  DEALLOCATE job_steps_cursor      -- For multiserver jobs, call the sp_post_msx_operation stored procedure to update  -- all the target servers. Note that the sp_post_msx_operation stored procedure is safe  -- to call because it verifies whether the job is really a multiserver job.  declare jobs_cursor CURSOR FOR      select job_id      from @jobsToUpdate    OPEN jobs_cursor  FETCH NEXT from jobs_cursor into @updated_job_id  WHILE (@@FETCH_STATUS <> -1)  BEGIN      IF (@@FETCH_STATUS <> -2)      BEGIN          EXEC sp_post_msx_operation @operation = 'UPDATE', @job_id = @updated_job_id      END      FETCH NEXT from jobs_cursor into @updated_job_id  END    CLOSE jobs_cursor  DEALLOCATE jobs_cursor  -- List the jobs that we ran on, including the previous command  -- text. We list all of the job steps, even the ones that we did not  -- update. Otherwise, a jumble of job steps from  -- different jobs run together and the output is not  -- useful.  select N'Warning - Jobs Updated' = N'The following job steps and job output file names were analyzed and potentially updated to add the ESCAPE_NONE macro before any job tokens that were not already escaped. Please review the modified job steps and replace ESCAPE_NONE with the correct escape macro.'    select suser_sname(jobs.owner_sid) as N'Job owner',          jobs.name as N'Job name',          jobs.job_id,          jobStepsUpdated.step_id,          N'Modified' = CASE WHEN jobStepsUpdated.command_new != jobStepsUpdated.command_old or jobStepsUpdated.output_file_new != jobStepsUpdated.output_file_old THEN 1 ELSE 0 END,         N'Command' = jobStepsUpdated.command_new,          N'Previous Command' = jobStepsUpdated.command_old,          N'Output file' = jobStepsUpdated.output_file_new,          N'Previous Output file' = jobStepsUpdated.output_file_old      from sysjobs as jobs, @jobsToUpdate as jobsUpdated, @jobStepsToUpdate as jobStepsUpdated      where jobsUpdated.job_id = jobs.job_id and jobsUpdated.job_id = jobStepsUpdated.job_id      order by 'Job name', jobStepsUpdated.step_idgo
Nachdem Sie das Skript ausgeführt haben, wird die sp_AddEscapeNoneToJobStepTokens gespeicherte Prozedur erstellt. Standardmäßig werden alle Aufträge aktualisiert, wenn Sie diese gespeicherte Prozedur ohne Parameter ausführen. Wenn Sie nur bestimmte Aufträge aktualisieren möchten, müssen Sie Werte ungleich NULL für mindestens einen der folgenden drei Parameter angeben:
  • @job_name
  • @job_id
  • @owner_name
So können Sie beispielsweise die folgende Syntax verwenden:
  • Alle Aufträge aktualisieren:
    EXEC sp_AddEscapeNoneToJobStepTokens
  • Aktualisieren eines Auftrags durch Angabe des Auftragsnamens:
    EXEC sp_AddEscapeNoneToJobStepTokens 'MyJob'
  • Aktualisieren von Aufträgen, die dem gleichen Besitzer gehören:
    EXEC sp_AddEscapeNoneToJobStepTokens null,null,'JobOwner'
Mit diesem Skript wird das ESCAPE_NONE -Makro allen Auftragsschritten hinzugefügt, die Token enthalten. Nachdem Sie dieses Skript ausgeführt haben, empfehlen wir, dass Sie Ihre Auftragsschritte überprüfen, die Token so schnell wie möglich verwenden. Ersetzen Sie dann das ESCAPE_NONE -Makro durch eines der anderen Escape-Makros, das für den Auftragsschritt Kontext geeignet ist.Hinweis Wenn Sie in einer Masterserver (MSX)-und einer TSX-Umgebung arbeiten, müssen Sie das Skript sowohl auf dem MSX als auch auf dem TSX ausführen, um sicherzustellen, dass Master Aufträge auf der TSX ordnungsgemäß aktualisiert werden. Weitere Informationen zum Aktualisieren von Aufträgen für die Verwendung der neuen Syntax und zur Verwendung von Escape-Makros zum Aktivieren der Token Ersetzung in Auftragsschritten des SQL Server-Agents finden Sie unter "Verwenden von Token in Auftragsschritten" in der SQL Server 2005-Onlinedokumentation (April 2006) oder in neueren Versionen der SQL Server 2005-Onlinedokumentation.

Status


Microsoft hat bestätigt, dass es sich hierbei um ein Problem bei den in diesem Artikel genannten Microsoft-Produkten handelt.

Informationsquellen


Weitere Informationen zum Verwenden von Token in Auftragsschritten finden Sie auf der folgenden Microsoft Developer Network (MSDN)-Website: