I once had a need to amend a procedure used by a report. The purpose was to enhance the report to have more fields displayed. My solution was straightforward: change the procedure logic to include the new fields, drop and recreate the procedure, and update the report file to include the added fields.
I was astonished to find that the procedure had syntax errors even before I started changing it. I tried to run the report with the original procedure in place and found the report was working fine. At this point I was confused with two main questions in my mind. One was how is the procedure working while it has syntax errors, and two, how did the first programmer manage to create the procedure and get it installed with the errors.
On my effort to solve the issue, I gained knowledge and experience that I want to share with others. I hope I can help others who may encounter the same problem and prevent them from spending a large amount of time diagnosing the issue.
The Actual Problem
Let us go to the actual problem that we need to address. I am using the AdventureWorks2012 database for my explanation. Run the code in LIsting 1 to create the procedure that we are going to use.
USE AdventureWorks2012; GO DECLARE @String NVARCHAR(MAX); --Generate a string for creating a procedure SET @String = 'CREATE PROCEDURE cProcWithLongStrings AS SELECT ' + char(39) + REPLICATE ('A',200) + char (39) + ' as First,' + char(39) + REPLICATE('B',243) + char(39) + ' as Secon,' + char(39) + REPLICATE('C',243) + char(39) + ' as Third,' + char(39) + REPLICATE('D',243) + char(39) + ' as Fourt,' + char(39) + REPLICATE('E',243) + char(39) + ' as Fifth;'; --Getting the script for creating the procedure PRINT @String --Install the procedure by executing the string. EXEC (@String ); GO
Listing 1
Below is the result as seen in Management Studio
Figure 1
Now we have our procedure created, we need to have a print result for the string used to create the procedure. This string is going to be stored in the text column of the sys.syscomments table. Let us find the length of the string. We can do that by copying the string to the word count application like charactercountonline.
Below is the copy of the print result
CREATE PROCEDURE cProcWithLongStrings AS SELECT 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' as First , 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' as Secon , 'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC' as Third , 'DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD' as Fourth , 'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE' as Fifth;
Listing 2
You will find that the count is 1280 characters as seen in the screen shot below.
Figure 2
Now let us execute the script to see the result
EXECUTE cProcWithLongStrings;
Upon executing the above statement we get the results shown in Figure 3.
Figure 3
The result above means that our procedure is working correctly, since the procedure run without errors and gave the expected output.
Now let us get the text for our procedure, which we will use to recreate the procedure. To do that we run the following code:
--Getting the created procedure using sp_helptext EXEC sp_helptext 'cProcWithLongStrings'; GO
Below (in Listing 3) is the result that you will get after running the code above
CREATE PROCEDURE cProcWithLongStrings AS SELECT'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' as F irst,'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' as S econd,'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC' as T hird,'DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD' as F ourth,'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE' as F ifth;
Listing 3
In a new code window run the following code to drop the cProcWithLongString procedure.
DROP PROCEDURE cProcWithLongStrings GO
Go back to the code window with results shown in listing 3 and run the code to recreate the cProcWithLongStringprocedure.
Upon recreating the procedure you will get the following error.
Msg 102, Level 15, State 1, Procedure cProcWithLongStrings, Line 2
Incorrect syntax near 'irst'.
If we look at the text output of the code in Listing 1 we can see that the whole procedure is only in one line with no syntax errors as seen in Figure 1. Using sp_helptext to recreate the procedure text from syscomments resulted to a procedure with syntax errors. This is because of algorithms used by sp_helptext to do its job. sp_helptext reads text from syscomments and appends a new line after every 255th character. In our procedure the first 255th character if F of the word first, and the second 255th character is S of the word second. The third 255th character is T of the word third, the fourth 255th character is F of the word fourth, and the last 255th character is F of the word fifth. Thus sp_helptext separated those word (in such a way that they read as F irst, S econd, T hird, F ourth, and F ifth) creating a syntax error since compiler do not accept white spaces for columns map names.
To workaround this problem you can remove the white spaces introduced by sp_helptext to form complete words. Sometimes this approach can be tedious if we have a complex procedure or if this problem is occurring frequently. In that case is better to have a separate copy of sp_helptext which do not append a new line after each 255th character.
Let us have a look on sp_helptext procedure to understand which change is needed to achieve that. Run the code below to get the procedure
sp_helptext sp_helptext
Below is the output of the query above
create procedure sys. sp_helptext @objname nvarchar (776) ,@columnname sysname = NULL as set nocount on declare @dbname sysname ,@objid int ,@BlankSpaceAdded int ,@BasePos int ,@CurrentPos int ,@TextLength int ,@LineId int ,@AddOnLen int ,@LFCR int --lengths of line feed carriage return ,@DefinedLength int /* NOTE: Length of @SyscomText is 4000 to replace the length of ** text column in syscomments. ** lengths on @Line, #CommentText Text column and ** value for @DefinedLength are all 255. These need to all have ** the same values. 255 was selected in order for the max length ** display using down level clients */,@SyscomText nvarchar(4000 ) ,@Line nvarchar(255 ) select @DefinedLength = 255 select @BlankSpaceAdded = 0 /*Keeps track of blank spaces at end of lines. Note Len function ignores trailing blank spaces*/CREATE TABLE #CommentText (LineId int ,Text nvarchar( 255) collate catalog_default ) /* ** Make sure the @objname is local to the current database. */select @dbname = parsename(@objname ,3) if @dbname is null select @dbname = db_name() else if @dbname <> db_name () begin raiserror(15250 ,-1,- 1) return (1 ) end /* ** See if @objname exists. */select @objid = object_id(@objname ) if (@objid is null) begin raiserror(15009 ,-1,- 1,@objname ,@dbname) return (1 ) end -- If second parameter was given. if ( @columnname is not null) begin -- Check if it is a table if (select count(*) from sys.objects where object_id = @objid and type in ( 'S ','U ' ,'TF'))= 0 begin raiserror(15218 ,-1,- 1,@objname ) return(1 ) end -- check if it is a correct column name if ((select 'count'= count(*) from sys .columns where name = @columnname and object_id = @objid) =0) begin raiserror(15645 ,-1,- 1,@columnname ) return(1 ) end if ( ColumnProperty(@objid , @columnname , 'IsComputed' ) = 0) begin raiserror(15646 ,-1,- 1,@columnname ) return(1 ) end declare ms_crs_syscom CURSOR LOCAL FOR select text from syscomments where id = @objid and encrypted = 0 and number = (select column_id from sys. columns where name = @columnname and object_id = @objid ) order by number, colid FOR READ ONLY end else if @objid < 0 -- Handle system-objects begin -- Check count of rows with text data if (select count(*) from master.sys .syscomments where id = @objid and text is not null) = 0 begin raiserror(15197 ,-1,- 1,@objname ) return (1 ) end declare ms_crs_syscom CURSOR LOCAL FOR select text from master.sys .syscomments where id = @objid ORDER BY number, colid FOR READ ONLY end else begin /* ** Find out how many lines of text are coming back, ** and return if there are none. */ if (select count(*) from syscomments c , sysobjects o where o .xtype not in ( 'S', 'U') and o .id = c .id and o .id = @objid ) = 0 begin raiserror(15197 ,-1,- 1,@objname ) return (1 ) end if (select count(*) from syscomments where id = @objid and encrypted = 0) = 0 begin raiserror(15471 ,-1,- 1,@objname ) return (0 ) end declare ms_crs_syscom CURSOR LOCAL FOR select text from syscomments where id = @objid and encrypted = 0 ORDER BY number, colid FOR READ ONLY end /* ** else get the text. */select @LFCR = 2 select @LineId = 1 OPEN ms_crs_syscom FETCH NEXT from ms_crs_syscom into @SyscomText WHILE @@fetch_status >= 0 begin select @BasePos = 1 select @CurrentPos = 1 select @TextLength = LEN( @SyscomText) WHILE @CurrentPos != 0 begin --Looking for end of line followed by carriage return select @CurrentPos = CHARINDEX(char (13)+ char(10 ), @SyscomText , @BasePos) --If carriage return found IF @CurrentPos != 0 begin /*If new value for @Lines length will be > then the **set length then insert current contents of @line **and proceed. */ while (isnull (LEN( @Line),0 ) + @BlankSpaceAdded + @CurrentPos-@BasePos + @LFCR) > @DefinedLength begin select @AddOnLen = @DefinedLength-(isnull (LEN( @Line),0 ) + @BlankSpaceAdded) INSERT #CommentText VALUES ( @LineId , isnull(@Line , N'' ) + isnull( SUBSTRING(@SyscomText , @BasePos , @AddOnLen), N'')) select @Line = NULL, @LineId = @LineId + 1, @BasePos = @BasePos + @AddOnLen , @BlankSpaceAdded = 0 end select @Line = isnull(@Line , N'' ) + isnull( SUBSTRING(@SyscomText , @BasePos, @CurrentPos-@BasePos + @LFCR), N'') select @BasePos = @CurrentPos+2 INSERT #CommentText VALUES( @LineId, @Line ) select @LineId = @LineId + 1 select @Line = NULL end else --else carriage return not found begin IF @BasePos <= @TextLength begin /*If new value for @Lines length will be > then the **defined length */ while (isnull (LEN( @Line),0 ) + @BlankSpaceAdded + @TextLength-@BasePos +1 ) > @DefinedLength begin select @AddOnLen = @DefinedLength - (isnull (LEN( @Line),0 ) + @BlankSpaceAdded) INSERT #CommentText VALUES ( @LineId , isnull(@Line , N'' ) + isnull( SUBSTRING(@SyscomText , @BasePos, @AddOnLen), N'')) select @Line = NULL, @LineId = @LineId + 1, @BasePos = @BasePos + @AddOnLen , @BlankSpaceAdded = 0 end select @Line = isnull(@Line , N'' ) + isnull(SUBSTRING (@SyscomText, @BasePos, @TextLength- @BasePos+1 ), N'' ) if LEN (@Line) < @DefinedLength and charindex( ' ', @SyscomText, @TextLength+1 ) > 0 begin select @Line = @Line + ' ', @BlankSpaceAdded = 1 end end end end FETCH NEXT from ms_crs_syscom into @SyscomText end IF @Line is NOT NULL INSERT #CommentText VALUES ( @LineId , @Line ) select Text from #CommentText order by LineId CLOSE ms_crs_syscom DEALLOCATE ms_crs_syscom DROP TABLE #CommentText return (0 ) -- sp_helptext
Listing 4
I will explain only some part of this procedure, you can take your own time to understand it better. From the top of the procedure you can see that there is number of declaration of variables. To achieve our goal we need to change length for @Line and value @DefinedLength parameters from 255 to 4000. @Line is the parameter that holds the lines of the printed output from sp_helptext. @DefinedLength is a parameter that holds maximum span length of the printed output from sp_helptext, this is why you can see the result from sp_helptext cProcWithLongStrings span to that length. @DefinedLength is used by the procedure to calculate the length of line to be printed out. We also need to change the length of Text field in #CommentText table to 4000.
Now go on and do the changes, also rename the procedure from sys.sp_helptext to dbo.custom_sp_helptext. Our new procedure will be this.
create procedure dbo. custom_sp_helptext @objname nvarchar (776) ,@columnname sysname = NULL as set nocount on declare @dbname sysname ,@objid int ,@BlankSpaceAdded int ,@BasePos int ,@CurrentPos int ,@TextLength int ,@LineId int ,@AddOnLen int ,@LFCR int --lengths of line feed carriage return ,@DefinedLength int /* NOTE: Length of @SyscomText is 4000 to replace the length of ** text column in syscomments. ** lengths on @Line, #CommentText Text column and ** value for @DefinedLength are all 255. These need to all have ** the same values. 255 was selected in order for the max length ** display using down level clients */,@SyscomText nvarchar(4000 ) ,@Line nvarchar(4000 ) select @DefinedLength = 4000 select @BlankSpaceAdded = 0 /*Keeps track of blank spaces at end of lines. Note Len function ignores trailing blank spaces*/CREATE TABLE #CommentText (LineId int ,Text nvarchar( 4000) collate catalog_default ) /* ** Make sure the @objname is local to the current database. */select @dbname = parsename(@objname ,3) if @dbname is null select @dbname = db_name() else if @dbname <> db_name () begin raiserror(15250 ,-1,- 1) return (1 ) end /* ** See if @objname exists. */select @objid = object_id(@objname ) if (@objid is null) begin raiserror(15009 ,-1,- 1,@objname ,@dbname) return (1 ) end -- If second parameter was given. if ( @columnname is not null) begin -- Check if it is a table if (select count(*) from sys.objects where object_id = @objid and type in ( 'S ','U ' ,'TF'))= 0 begin raiserror(15218 ,-1,- 1,@objname ) return(1 ) end -- check if it is a correct column name if ((select 'count'= count(*) from sys .columns where name = @columnname and object_id = @objid) =0) begin raiserror(15645 ,-1,- 1,@columnname ) return(1 ) end if ( ColumnProperty(@objid , @columnname , 'IsComputed' ) = 0) begin raiserror(15646 ,-1,- 1,@columnname ) return(1 ) end declare ms_crs_syscom CURSOR LOCAL FOR select text from syscomments where id = @objid and encrypted = 0 and number = (select column_id from sys. columns where name = @columnname and object_id = @objid ) order by number, colid FOR READ ONLY end else if @objid < 0 -- Handle system-objects begin -- Check count of rows with text data if (select count(*) from master.sys .syscomments where id = @objid and text is not null) = 0 begin raiserror(15197 ,-1,- 1,@objname ) return (1 ) end declare ms_crs_syscom CURSOR LOCAL FOR select text from master.sys .syscomments where id = @objid ORDER BY number, colid FOR READ ONLY end else begin /* ** Find out how many lines of text are coming back, ** and return if there are none. */ if (select count(*) from syscomments c , sysobjects o where o .xtype not in ( 'S', 'U') and o .id = c .id and o .id = @objid ) = 0 begin raiserror(15197 ,-1,- 1,@objname ) return (1 ) end if (select count(*) from syscomments where id = @objid and encrypted = 0) = 0 begin raiserror(15471 ,-1,- 1,@objname ) return (0 ) end declare ms_crs_syscom CURSOR LOCAL FOR select text from syscomments where id = @objid and encrypted = 0 ORDER BY number, colid FOR READ ONLY end /* ** else get the text. */select @LFCR = 2 select @LineId = 1 OPEN ms_crs_syscom FETCH NEXT from ms_crs_syscom into @SyscomText WHILE @@fetch_status >= 0 begin select @BasePos = 1 select @CurrentPos = 1 select @TextLength = LEN( @SyscomText) WHILE @CurrentPos != 0 begin --Looking for end of line followed by carriage return select @CurrentPos = CHARINDEX(char (13)+ char(10 ), @SyscomText , @BasePos) --If carriage return found IF @CurrentPos != 0 begin /*If new value for @Lines length will be > then the **set length then insert current contents of @line **and proceed. */ while (isnull (LEN( @Line),0 ) + @BlankSpaceAdded + @CurrentPos-@BasePos + @LFCR) > @DefinedLength begin select @AddOnLen = @DefinedLength-(isnull (LEN( @Line),0 ) + @BlankSpaceAdded) INSERT #CommentText VALUES ( @LineId , isnull(@Line , N'' ) + isnull( SUBSTRING(@SyscomText , @BasePos , @AddOnLen), N'')) select @Line = NULL, @LineId = @LineId + 1, @BasePos = @BasePos + @AddOnLen , @BlankSpaceAdded = 0 end select @Line = isnull(@Line , N'' ) + isnull( SUBSTRING(@SyscomText , @BasePos, @CurrentPos-@BasePos + @LFCR), N'') select @BasePos = @CurrentPos+2 INSERT #CommentText VALUES( @LineId, @Line ) select @LineId = @LineId + 1 select @Line = NULL end else --else carriage return not found begin IF @BasePos <= @TextLength begin /*If new value for @Lines length will be > then the **defined length */ while (isnull (LEN( @Line),0 ) + @BlankSpaceAdded + @TextLength-@BasePos +1 ) > @DefinedLength begin select @AddOnLen = @DefinedLength - (isnull (LEN( @Line),0 ) + @BlankSpaceAdded) INSERT #CommentText VALUES ( @LineId , isnull(@Line , N'' ) + isnull( SUBSTRING(@SyscomText , @BasePos, @AddOnLen), N'')) select @Line = NULL, @LineId = @LineId + 1, @BasePos = @BasePos + @AddOnLen , @BlankSpaceAdded = 0 end select @Line = isnull(@Line , N'' ) + isnull(SUBSTRING (@SyscomText, @BasePos, @TextLength- @BasePos+1 ), N'' ) if LEN (@Line) < @DefinedLength and charindex( ' ', @SyscomText, @TextLength+1 ) > 0 begin select @Line = @Line + ' ', @BlankSpaceAdded = 1 end end end end FETCH NEXT from ms_crs_syscom into @SyscomText end IF @Line is NOT NULL INSERT #CommentText VALUES ( @LineId , @Line ) select Text from #CommentText order by LineId CLOSE ms_crs_syscom DEALLOCATE ms_crs_syscom DROP TABLE #CommentText return (0 ) -- sp_helptext --
Listing 5
Run the procedure and test it to see if we have fixed the problem. It should work now. Let us now rerun our cProcWithLongStrings this new version of sp_helptext.
EXEC custom_sp_helptext 'cProcWithLongStrings'
Now we have a result that do not have syntax errors.
CREATE PROCEDURE [dbo].[cProcWithLongStrings] AS SELECT 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' as First,'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB' as Secon,'CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC' as Third,'DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD' as Fourt,'EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE' as Fifth;