Pekerjaan agen Server SQL gagal saat pekerjaan yang berisi langkah-langkah pekerjaan yang menggunakan token setelah Anda menginstal SQL Server 2005 Service Pack 1

Bug #: 426808 (SQLBUDT)

Gejala

Setelah Anda menginstal Microsoft SQL Server 2005 Paket Layanan 1 (SP1), Anda mengalami perilaku berikut ini:

  • Pekerjaan agen Server SQL gagal ketika pekerjaan yang berisi langkah pekerjaan yang menggunakan token.

  • Anda menerima pesan galat berikut ini:

    Langkah kerja berisi satu atau beberapa Token. Untuk SQL Server 2005 Service Pack 1 atau versi yang lebih baru, semua langkah pekerjaan dengan token harus diperbarui dengan makro sebelum pekerjaan bisa berjalan.

Catatan Masalah ini terjadi dengan Build 2046 atau versi SQL Server 2005 yang lebih baru.

Penyebab

Di SQL Server 2005 SP1, sintaks token langkah kerja agen Server SQL telah berubah. Sekarang, Anda harus menyertakan makro Escape dengan semua Token yang digunakan dalam langkah kerja. Jika Anda tidak menyertakan makro keluar, langkah pekerjaan tersebut akan gagal. Tabel berikut ini mencantumkan makro Escape.

Meloloskan makro

Deskripsi

$(ESCAPE_SQUOTE(TokenName))

Tanda kutip lolos apostrof (') dalam string pengganti Token. Makro menggantikan satu tanda kutip dengan dua tanda kutip.

$(ESCAPE_DQUOTE(TokenName))

Tanda kutip lolos makro ini (") dalam string pengganti Token. Makro menggantikan satu tanda kutip dengan dua tanda kutip.

$(ESCAPE_RBRACKET(TokenName))

Makro ini lolos kurung siku kanan (]) dalam string pengganti Token. Makro menggantikan satu braket kanan dengan dua kurung siku kanan.

$ (ESCAPE_NONE (Tokenname))

Makro mengganti token tanpa meloloskan karakter apa pun dalam string. Makro ini disediakan untuk mendukung kompatibilitas mundur di lingkungan tempat string pengganti token hanya diharapkan dari pengguna tepercaya.

Misalnya, langkah kerja mungkin berisi pernyataan Transact-SQL berikut yang menggunakan token a-DBN :

CREATE DATABASE [$(A-DBN)]

Dalam contoh ini, Anda harus memperbarui sintaks token ke sintaks berikut:

CREATE DATABASE [$(ESCAPE_RBRACKET(A-DBN))]

Perubahan ini berbeda dari perilaku SQL Server 2005 sebelumnya, di mana makro Escape tidak diperlukan.

Pemecahan Masalah

Untuk mengatasi masalah ini, perbarui semua atau hanya pekerjaan tertentu yang menggunakan token ke sintaks token baru. Untuk melakukan ini, gunakan prosedur sp_AddEscapeNoneToJobStepTokens disimpan. Anda dapat membuat prosedur yang disimpan ini menggunakan skrip Transact-SQL berikut ini.Catatan Pastikan bahwa Build SQL Server 2005 SP1 yang Anda instal adalah Build 2046 atau Build yang lebih baru. Selain itu, Anda harus menjadi anggota peran server tetap sysadmin untuk menjalankan skrip.

      -- 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

Setelah menjalankan skrip, prosedur sp_AddEscapeNoneToJobStepTokens disimpan telah dibuat. Secara default, Anda akan memperbarui semua pekerjaan jika menjalankan prosedur yang disimpan tanpa parameter apa pun. Jika Anda hanya ingin memperbarui pekerjaan tertentu, Anda harus menentukan nilai non-null setidaknya untuk salah satu dari tiga parameter berikut ini:

  • @job_name

  • @job_id

  • @owner_name

Misalnya, Anda dapat menggunakan sintaks berikut ini:

  • Perbarui semua pekerjaan:

    EXEC sp_AddEscapeNoneToJobStepTokens
  • Memperbarui pekerjaan dengan menentukan nama pekerjaan:

    EXEC sp_AddEscapeNoneToJobStepTokens 'MyJob'
  • Memperbarui pekerjaan yang dimiliki oleh pemilik yang sama:

    EXEC sp_AddEscapeNoneToJobStepTokens null,null,'JobOwner'

Skrip ini menambahkan makro ESCAPE_NONE ke semua langkah pekerjaan yang berisi token. Setelah menjalankan skrip ini, kami menyarankan agar Anda meninjau langkah-langkah pekerjaan yang menggunakan token sesegera mungkin. Lalu, ganti makro ESCAPE_NONE dengan salah satu makro Escape lainnya yang sesuai untuk konteks langkah kerja.Catatan Jika Anda bekerja di lingkungan server master (MSX) dan target server (TSX), Anda harus menjalankan skrip ini pada MSX dan TSX untuk memastikan bahwa pekerjaan Master di TSX diperbarui dengan benar. Untuk informasi selengkapnya tentang cara memperbarui pekerjaan untuk menggunakan sintaks baru dan cara menggunakan Escape Macros untuk mengaktifkan penggantian token dalam langkah kerja agen Server SQL, lihat topik "menggunakan token dalam langkah kerja" di SQL Server 2005 Books Online (April 2006) atau versi SQL Server 2005 Books Online.

Status

Microsoft telah mengonfirmasi bahwa ini adalah masalah pada produk Microsoft yang tercantum di bagian "Berlaku untuk".

Referensi

Untuk informasi selengkapnya tentang cara menggunakan token dalam langkah-langkah pekerjaan, kunjungi situs web Microsoft Developer Network (MSDN) berikut:

http://msdn2.microsoft.com/en-us/library/ms175575.aspx

Perlu bantuan lainnya?

Kembangkan keterampilan Anda
Jelajahi pelatihan
Dapatkan fitur baru terlebih dahulu
Gabung Microsoft Insider

Apakah informasi ini bermanfaat?

Terima kasih atas umpan balik Anda!

Terima kasih atas umpan balik Anda! Sepertinya menghubungkan Anda ke salah satu agen dukungan Office kami akan sangat membantu.

×