Les tâches de l’agent SQL Server échouent lorsque les tâches contiennent des étapes de travail qui utilisent des jetons après l’installation de SQL Server 2005 Service Pack 1


Erreur n ° : 426808 (SQLBUDT)

Symptômes


Après l’installation de Microsoft SQL Server 2005 Service Pack 1 (SP1), vous observez le comportement suivant :
  • Les tâches de l’agent SQL Server échouent lorsque les tâches contiennent des étapes de travail qui utilisent des jetons.
  • Le message d’erreur suivant s’affiche :
    L’étape de travail contient un ou plusieurs jetons. Pour SQL Server 2005 Service Pack 1 ou version ultérieure, vous devez mettre à jour toutes les étapes de travail avec des jetons pour pouvoir exécuter une macro.
Remarque Ce problème se produit avec la version 2046 ou ultérieure de SQL Server 2005.

Cause


Dans SQL Server 2005 SP1, la syntaxe jeton de l’étape de travail de l’agent SQL Server a changé. À présent, vous devez inclure une macro Escape avec tous les jetons utilisés dans les étapes de travail. Si vous n’incluez pas de macro Escape, celles-ci échoueront. Le tableau suivant répertorie les macros Escape.
Macro EscapeDescription
$(ESCAPE_SQUOTE(TokenName))Cette macro utilise des apostrophes (') dans la chaîne de remplacement de jeton. La macro remplace une apostrophe par deux apostrophes.
$(ESCAPE_DQUOTE(TokenName))Cette macro utilise des guillemets (") dans la chaîne de remplacement de jeton. La macro remplace une apostrophe par des guillemets doubles.
$(ESCAPE_RBRACKET(TokenName))Cette macro utilise les crochets vers la droite (]) dans la chaîne de remplacement de jeton. La macro remplace un crochet fermant par deux crochets droits.
$ (ESCAPE_NONE (TokenName))La macro remplace un jeton sans échappement aucun caractère de la chaîne. Cette macro est fournie pour prendre en charge la compatibilité descendante dans les environnements dans lesquels les chaînes de remplacement de jeton ne sont attendues que par les utilisateurs approuvés.
Par exemple, une étape de travail doit contenir l’instruction Transact-SQL suivante qui utilise le jeton a-DBN :
CREATE DATABASE [$(A-DBN)]
Dans cet exemple, vous devez mettre à jour la syntaxe de jeton vers la syntaxe suivante :
CREATE DATABASE [$(ESCAPE_RBRACKET(A-DBN))]
Ce changement diffère du comportement antérieur de SQL Server 2005, où les macros d’échappement n’étaient pas nécessaires.

Résolution


Pour résoudre ce problème, procédez à la mise à jour de toutes les tâches spécifiques utilisant des jetons vers la nouvelle syntaxe de jeton. Pour ce faire, utilisez la procédure stockée sp_AddEscapeNoneToJobStepTokens . Vous pouvez créer cette procédure stockée en utilisant le script Transact-SQL suivant.Remarque Assurez-vous que la build SQL Server 2005 SP1 que vous installez est Build 2046 ou une version ultérieure. Par ailleurs, vous devez être membre du rôle serveur fixe sysadmin pour exécuter le script.
      -- 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
Après avoir exécuté le script, la procédure stockée sp_AddEscapeNoneToJobStepTokens est créée. Par défaut, toutes les tâches sont mises à jour si vous exécutez cette procédure stockée sans paramètre. Si vous souhaitez mettre à jour uniquement des tâches spécifiques, vous devez spécifier des valeurs non null pour au moins l’un des trois paramètres suivants :
  • @job_name
  • @job_id
  • @owner_name
Par exemple, vous pouvez utiliser la syntaxe suivante :
  • Mettre à jour toutes les tâches :
    EXEC sp_AddEscapeNoneToJobStepTokens
  • Pour mettre à jour un travail, spécifiez le nom du poste :
    EXEC sp_AddEscapeNoneToJobStepTokens 'MyJob'
  • Mettez à jour les tâches appartenant au même propriétaire :
    EXEC sp_AddEscapeNoneToJobStepTokens null,null,'JobOwner'
Ce script ajoute la macro ESCAPE_NONE à toutes les étapes de travail qui contiennent des jetons. Après avoir exécuté ce script, nous vous conseillons de passer en revue les étapes de votre travail qui utilisent des jetons le plus rapidement possible. Remplacez ensuite la macro ESCAPE_NONE par l’une des autres macros Escape adaptées au contexte de l’étape de travail.Remarque Si vous travaillez dans un environnement serveur maître (MSX) et serveur cible (TSX), vous devez exécuter ce script sur le MSX et sur le TSX pour vous assurer que les tâches principales du serveur TSX sont correctement mises à jour. Pour plus d’informations sur la façon de mettre à jour des tâches pour utiliser la nouvelle syntaxe et la façon d’utiliser des macros d’échappement pour activer le remplacement de jeton dans les étapes de travail de l’agent SQL Server, voir la rubrique « utilisation de jetons dans les étapes de travail » dans la 2005 2006 documentation de SQL Server 2005 Books

Statut


Microsoft a confirmé l’existence de ce problème dans les produits Microsoft figurant dans la liste des produits concernés par cet article.

Références


Pour plus d’informations sur l’utilisation des jetons dans les étapes de travail, visitez le site Web Microsoft Developer Network (MSDN) suivant :