Skip to content

Commit 3d03b0f

Browse files
committed
Updates na sp_showcode e add nova proc utilidades de parse
1 parent 8d1a6e9 commit 3d03b0f

File tree

2 files changed

+135
-19
lines changed

2 files changed

+135
-19
lines changed

Misc/ParseStringExprVirgula.sql

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*#info
2+
3+
# Autor
4+
Rodrigo Ribeiro Gomes
5+
6+
# Descrição
7+
Um simples script (que pode ser transfromado em proc, funcao, etc. para fazer o parse de string no formato:
8+
OPCAO1,OPCAO2,OPCAO3,OPCAO\,ESCAPE,OPCAO-BARRA-FIM\\,BARRA\\ESCAPE
9+
10+
O script quebra uma string nas virgulas. Caso queria incluir uma virgula, escpae com \,.
11+
Se vc quiser incluir um "\," (barra segudo de virgula) escape a barra e a virgula \\ + \,
12+
13+
Assim, você tem um pequeno interpreador de expressoes que pode ser usado em procedures.
14+
A primeira ideia que vi isso nas procedures do Ola Hallegren, onde vc pode especificar expressoes como %,-sys,-%teste% para filtrar banco.
15+
Nunca vi a implementação dele, e resolvi deixar aqui uma versão minha que você pode usar e adaptar onde precisar (procs, funcoes, etc.)
16+
17+
O script inclui uma pequena validação do resultado esperado Não esquea de remover a validação ao usar.
18+
*/
19+
20+
21+
declare
22+
@Expr nvarchar(max) = N'%,Isso é um escape\,e vai continuar aqui!,Barra no fim:\\,Barra no \ meio!,Barra com virgual no fim:\\\,,Unicode❤️Chars,EncerradoComBarra\'
23+
24+
25+
26+
declare
27+
@i int = 0
28+
,@len int = len(@expr)
29+
,@CurrentChar nvarchar(1), @NextChar nvarchar(1)
30+
,@buff nvarchar(max) = ''
31+
32+
declare @Result table(id int identity not null,expr nvarchar(max))
33+
34+
35+
while @i <= @len
36+
begin
37+
set @i += 1;
38+
set @CurrentChar = substring(@expr+',',@i,1)
39+
set @NextChar = substring(@expr,@i+1,1)
40+
41+
if @CurrentChar = '\' and @NextChar in ('\',',')
42+
select @buff += @NextChar, @i += 1;
43+
else if @CurrentChar = ','
44+
begin
45+
insert into @Result(expr) values(@buff)
46+
set @buff = '';
47+
end else
48+
set @buff += @CurrentChar
49+
50+
end
51+
52+
53+
declare
54+
@Esperado table (id int identity not null,expr nvarchar(max))
55+
56+
insert into @Esperado values
57+
(N'%'),(N'Isso é um escape,e vai continuar aqui!')
58+
,(N'Barra no fim:\'),(N'Barra no \ meio!')
59+
,(N'Barra com virgual no fim:\,')
60+
,(N'Unicode❤️Chars')
61+
,(N'EncerradoComBarra\')
62+
63+
64+
select
65+
*
66+
,OK = case
67+
when e.expr = r.expr collate Latin1_General_BIN then 1
68+
else 0
69+
end
70+
from
71+
@Esperado e
72+
full join
73+
@Result r
74+
on e.id = r.id

Modulos/sp.showcode.sql

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,11 @@ ALTER PROC sp_showcode (
274274
,-- include descriptive headers. NOt used when mode is sp_helptext and xml.
275275
@headers bit = 1
276276

277+
,-- force @text being trated as a literal, with no filter or expression. In another words, you are escaping entire @text param, losing its powers
278+
-- With that, proc will behaves almost sp_helptext, finding exact object name
279+
-- Remember it is a complete literal. The text must follow rules of parsename function, that is it, [schema].[object]. Literal [ or ] require use ", for example "[abc]" or ["abc"] for inverse
280+
@literal bit = 0
281+
277282
-- Enable some messages for debugging.
278283
,@Debug bit = 0
279284
)
@@ -364,25 +369,36 @@ CREATE TABLE #sp_showcode_filters (
364369
,FilterObject nvarchar(4000)
365370
,FilterColumn nvarchar(4000)
366371
,ExprReal nvarchar(max)
367-
,IsWild as convert(bit,case when charindex('%',exprreal) > 0 then 1 else 0 end)
372+
,IsWild bit
368373
);
369374

370-
while @i <= @TextLen
375+
376+
while @i <= @TextLen and @literal = 0
371377
begin
372378
set @i += 1;
373379
set @CurrentChar = substring(@text+',',@i,1)
374380
set @NextChar = substring(@text,@i+1,1)
375381

376382
if @CurrentChar = N'\' and @NextChar in (N'\',N',')
377383
select @buff += @NextChar, @i += 1;
384+
else if @CurrentChar = '\' and @NextChar = '%' -- if found a \%, it is escaped. We handle this separated to avoid set @IsWild = 1
385+
select @buff += N'\%', @i += 1
378386
else if @CurrentChar = N','
379387
begin
380-
insert into #sp_showcode_filters(expr) values(@buff)
381-
set @buff = '';
382-
end else
388+
insert into #sp_showcode_filters(expr,IsWild) values(@buff,@IsWild)
389+
select @buff = '', @IsWild = 0
390+
end else begin
391+
392+
if @CurrentChar = '%' --never reaches if \%, because when \ is found, handle in else above.
393+
set @IsWild = 1
394+
383395
set @buff += @CurrentChar
396+
end
384397
end
385398
399+
if @literal = 1
400+
insert into #sp_showcode_filters(expr,IsWild) values (@text,0);
401+
386402
387403
update f
388404
set
@@ -397,7 +413,7 @@ from
397413
cross apply (
398414
SELECT
399415
IsNeg = CASE
400-
WHEN left(expr,1) = '-' THEN 1
416+
WHEN @literal = 0 AND left(expr,1) = '-' THEN 1
401417
ELSE 0
402418
END
403419
,ExprReal = CASE
@@ -410,18 +426,20 @@ from
410426
BF.FilterDb
411427
,BF.FilterSchema
412428
,FilterObject = CASE
429+
WHEN @literal = 1 THEN FilterObject
413430
WHEN ColSepIndex > 0 THEN LEFT(BF.FilterObject,ColSepIndex-1)
414431
ELSE BF.FilterObject
415432
END
416-
,FilterColumn = CASE
433+
,FilterColumn = CASE
434+
WHEN @literal = 1 THEN NULL
417435
WHEN ColSepIndex > 0 THEN SUBSTRING(BF.FilterObject,ColSepIndex+1,4000)
418436
ELSE '%'
419437
END
420438
FROM (
421439
SELECT
422440
FilterDb = isnull(parsename(E.ExprReal,3),db_name())
423441
,FilterSchema = parsename(E.ExprReal,2)
424-
,FilterObject = parsename(E.ExprReal,1)
442+
,FilterObject = isnull(parsename(E.ExprReal,1),@text)
425443
,ColSepIndex = CHARINDEX('/',parsename(E.ExprReal,1))
426444
) BF
427445
@@ -431,6 +449,10 @@ WHERE
431449
432450
SET @UserFilterCount = @@ROWCOUNT
433451
452+
set @IsWild = 0
453+
if exists(select * from #sp_showcode_filters where IsWild = 1)
454+
set @IsWild = 1
455+
434456
435457
-- if not explicit positive schema filter
436458
if not exists(select * from #sp_showcode_filters where IsNeg = 0 and FilterSchema is not null) and @IsWild = 1
@@ -444,7 +466,7 @@ end
444466
--
445467
UPDATE #sp_showcode_filters
446468
SET
447-
FilterSchema = ISNULL(FilterSchema,'%')
469+
FilterSchema = ISNULL(FilterSchema,CASE WHEN @literal = 1 THEN SCHEMA_NAME() ELSE '%' END)
448470
449471
450472
IF @Debug = 1
@@ -457,7 +479,7 @@ begin
457479
end
458480
459481
-- If user specified only non wild filter!
460-
if @UserFilterCount = (select count(*) from #sp_showcode_filters where IsWild = 0 and expr is not null)
482+
if @UserFilterCount = (select count(*) from #sp_showcode_filters where IsWild = 0 and expr is not null)
461483
begin
462484
set @all = 1;
463485
if @Debug = 1 raiserror('Changed @all to 1 due user explicit multiple filter.',0,1) with nowait;
@@ -532,6 +554,9 @@ BEGIN
532554
return;
533555
END
534556

557+
558+
559+
535560
DECLARE @DbList TABLE(Seq int, DbName sysname);
536561

537562
INSERT INTO @DbList(Seq,DBName)
@@ -563,6 +588,7 @@ WHERE
563588
f.IsNeg = 0
564589
)
565590

591+
566592
IF @Debug = 1
567593
SELECT * FROM @DbList
568594

@@ -629,7 +655,7 @@ BEGIN
629655
RAISERROR('Searching in db %s',0,1,@DbName) with nowait;
630656

631657

632-
set @sql = '
658+
set @sql = N'
633659
SELECT '+ISNULL('TOP('+CONVERT(varchar(10),@LeftLimit)+')','')+'
634660
DB_NAME()
635661
,O.name
@@ -705,24 +731,40 @@ BEGIN
705731
) I
706732
) A
707733
WHERE
708-
0 = (
734+
735+
'+CASE WHEN @literal = 1 THEN
736+
'
737+
EXISTS (
738+
SELECT -- literal enabled
739+
*
740+
FROM
741+
#sp_showcode_filters F
742+
WHERE
743+
F.FilterObject = O.name COLLATE DATABASE_DEFAULT
744+
AND
745+
F.FilterSchema = S.name COLLATE DATABASE_DEFAULT
746+
)
747+
'
748+
ELSE
749+
'0 = (
709750
SELECT
710751
max(convert(int,IsNeg))
711752
FROM
712753
#sp_showcode_filters F
713754
WHERE
714-
O.name LIKE F.FilterObject COLLATE DATABASE_DEFAULT
755+
O.name LIKE F.FilterObject COLLATE DATABASE_DEFAULT ESCAPE ''\''
715756
AND
716-
S.name LIKE F.FilterSchema COLLATE DATABASE_DEFAULT
757+
S.name LIKE F.FilterSchema COLLATE DATABASE_DEFAULT ESCAPE ''\''
717758
AND
718-
DB_NAME() LIKE F.FilterDb COLLATE DATABASE_DEFAULT
759+
DB_NAME() LIKE F.FilterDb COLLATE DATABASE_DEFAULT ESCAPE ''\''
719760
AND
720-
isnull(O.ColName,'''') LIKE F.FilterColumn COLLATE DATABASE_DEFAULT
761+
isnull(O.ColName,'''') LIKE F.FilterColumn COLLATE DATABASE_DEFAULT ESCAPE ''\''
721762

722763
)
764+
'
765+
END+
723766
724-
AND
725-
(
767+
'AND (
726768
(
727769
O.type in (''P'',''FN'',''IF'',''TF'',''V'',''TR'')
728770
AND
@@ -754,7 +796,7 @@ BEGIN
754796
RAISERROR(' Found: %d objects (total = %d), Top: %d, LeftLimit: %d. StartId: %d',0,1,@FoundCount,@TotalFound,@top,@LeftLimit,@StartId) with nowait;
755797
756798
-- If user specified just 1 filter, without wildcards, and we found somehting, we assume it searching extactly object name!
757-
IF @Seq = 1 AND @IsWild = 0 and @FoundCount > 0 AND @UserFilterCount = 1
799+
IF @Seq = 1 AND @IsWild = 0 and @FoundCount = 1 AND @UserFilterCount = 1
758800
break;
759801
760802
-- if top enabled and no more

0 commit comments

Comments
 (0)