<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Mauro Dalfreddo &#187; Hack</title>
	<atom:link href="http://www.maurodalfreddo.it/archives/tag/hack/feed" rel="self" type="application/rss+xml" />
	<link>http://www.maurodalfreddo.it</link>
	<description>My work, my life... my Blog</description>
	<lastBuildDate>Wed, 04 May 2011 21:00:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Grazie Google</title>
		<link>http://www.maurodalfreddo.it/archives/406/grazie-google</link>
		<comments>http://www.maurodalfreddo.it/archives/406/grazie-google#comments</comments>
		<pubDate>Wed, 22 Sep 2010 20:42:24 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Backup]]></category>
		<category><![CDATA[Hack]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=406</guid>
		<description><![CDATA[Ho recuperato alcuni contenuti del sito persi, grazie alla cache delle pagine dei motori di ricerca. <a href="http://www.maurodalfreddo.it/archives/406/grazie-google">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><em>Come sono riuscito a ripristinare il mio sito da una situazione catastrofica</em></p>
<div id="attachment_408" class="wp-caption alignnone" style="width: 510px"><img class="size-full wp-image-408" title="Altopiano di Asiago 2010" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/montagna500b.jpg" alt="" width="500" height="361" /><p class="wp-caption-text">Altopiano di Asiago 2010</p></div>
<p>Ho trascorso le ferie di fine agosto in montagna. Tornando, una decina di giorni fà, ho ricevuto una newsletter dal fornitore dell&#8217;hosting nella quale veniva fatto riferimento ad alcuni disagi subiti da alcuni utenti a causa del crash del database.</p>
<p>Rimanendo un po&#8217; perplesso, ma giusto per curiosità, ho aperto col browser il mio sito, trascurato per molte settimane. </p>
<p>Ho sgranato gli occhi leggendo su una pagina semi-bianca il titolo  &#8220;<em>jhjhlooddwwd: Solo un altro sito WordPress</em>&#8221; !</p>
<p><strong>Orrore e tragedia</strong>! Pochi istanti e la conferma dei tecnici dell&#8217;Hosting provider<br />
e ho capito che il database del mio sito era uno di quelli sfortunati, cioè si era in pratico &#8220;fumato&#8221; a causa di un grave errore hardware del server, ma soprattutto che i dati non potevano essere ripristinati!</p>
<p>Chi predica bene razzola male: non avevo impostato backup periodici, e l&#8217;ultimo backup che avevo fatto manualmente al DB era purtroppo abbastanza vecchio da non contenere alcuni ultimi post! Accidentaccio&#8230; il contenuto di qualche articolo, a mio avviso, era veramente interessante ed era un peccato che fosse stato perso così.</p>
<p>Per fortuna che alcune idee buone vengono al momento giusto: grazie alla cache delle pagine nei motori di ricerca e<br />
grazie anche alla latenza con la quale i crawlers effettuano la scansione dei siti, sono riuscito fortunosamente a recuperare e salvare tutti i contenuti persi.<br />
Se la velocità di scansione fosse stata più elevata, anche per esempio settimanale, avrei recuperato solo parzialmente le informazioni: non tutti i mali vengono per nuocere quindi!</p>
<p>Grazie allora a</p>
<p> <a href="http://www.google.it" target="_blank">Google</a>,  <a href="http://www.bing.com/" target="_blank">Bing</a> e  <a href="http://www.gigablast.com/" target="_blank">Gigablast</a></p>
<p>non lo conoscevo, ma ho utilizzato anche Gigablast, The &#8220;Green&#8221; Search Engine, la cui server farm viene alimentata al 90% dall&#8217;energia pulita del vento.<br />
Poi, per non affidarmi esclusivamente a backup manuali, ho creato opportuni script per il backup:<br />
Il database non è accedibile direttamente, ma solo via Web da MySqlAdmin e accedendo precedentemente da CPanel:<br />
certamente sono precauzioni legate alla sicurezza, ma impediscono l&#8217;implementazione di un automatismo semplice.</p>
<p>Utilizzando Wget in 3 passi, si riesce a scaricare un backup gzippato del DB, simulando le stesse azioni che farebbe un utente reale.</p>
<p>Di seguito riporto il contenuto dello script backup.sh:</p>
<pre><span style="color: #008000;">-- cpanel url
cpurl=https://cp.tophost.it/
cpusr=user1
cppwd=pass1</span></pre>
<pre><span style="color: #008000;">-- mysqladmin url
dburl=$cpurl/mysql/
dbusr=database_user
dbpwd=database_pass</span></pre>
<pre><span style="color: #008000;">-- file location
d=$(dirname $0)
bkfile="backup.sql-$(date +%F).gz"
bkurl="$dburl/export.php?export_type=server&amp;db_select%5B%5D=database_user&amp;what=s
ql&amp;header_comment=&amp;sql_compat=NONE&amp;sql_structure=structure&amp;sql_auto_increment=1&amp;
use_backquotes=1&amp;sql_data=data&amp;showcolumns=yes&amp;extended_ins=yes&amp;max_query_size=5
0000&amp;hexforbinary=yes&amp;sql_type=insert&amp;latex_caption=yes&amp;latex_structure=structur
e&amp;latex_structure_caption=Struttura+della+tabella+__TABLE__&amp;latex_structure_cont
inued_caption=Struttura+della+tabella+__TABLE__+%28continua%29&amp;latex_structure_l
abel=tab%3A__TABLE__-structure&amp;latex_data=data&amp;latex_showcolumns=yes&amp;latex_data_
caption=Contenuto+della+tabella+__TABLE__&amp;latex_data_continued_caption=Contenuto
+della+tabella+__TABLE__+%28continua%29&amp;latex_data_label=tab%3A__TABLE__-data&amp;la
tex_replace_null=%5Ctextit%7BNULL%7D&amp;csv_data=csv_data&amp;export_separator=%3B&amp;encl
osed=%22&amp;escaped=%5C&amp;add_character=%5Cr%5Cn&amp;csv_replace_null=NULL&amp;excel_data=exc
el_data&amp;excel_replace_null=NULL&amp;excel_edition=win&amp;htmlexcel_data=htmlexcel_data&amp;
htmlexcel_replace_null=NULL&amp;htmlword_structure=structure&amp;htmlword_data=data&amp;html
word_replace_null=NULL&amp;pdf_data=pdf_data&amp;pdf_report_title=&amp;xml_data=xml_data&amp;asf
ile=sendit&amp;filename_template=__SERVER__&amp;remember_template=on&amp;compression=gzip"</span><span style="color: #008000;"> </span></pre>
<pre><span style="color: #008000;">/usr/bin/wget  --save-cookies=c --keep-session-cookies --http-user=$cpusr --http
-passwd=$cppwd -O $d/u1 -q $cpurl
/usr/bin/wget  --save-cookies=c1 --load-cookies=c --keep-session-cookies --http-
user=$dbusr --http-passwd=$dbpwd -O $d/u2 -q $dburl
/usr/bin/wget  --save-cookies=c2 --load-cookies=c1 --keep-session-cookies --http
-user=$dbusr --http-passwd=$dbpwd -O $d/$bkfile -q $bkurl</span></pre>
<p> <br />
Per quanto riguarda la copia del filesystem ho utilizzato Lftp.</p>
<pre><span style="color: #008000;">echo "open ftp://$login:$password@$desthost" &gt; $tmpfile
echo "mirror -ec / $d/MIRROR/" &gt;&gt; $tmpfile
echo "exit" &gt;&gt; $tmpfile</span></pre>
<pre><span style="color: #008000;">/usr/bin/lftp -f  $tmpfile</span></pre>
<p> </p>
<div id="attachment_413" class="wp-caption alignnone" style="width: 510px"><img class="size-full wp-image-413" title="Pomeriggio d'agosto sull'Altopiano" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/sereno500.jpg" alt="" width="500" height="369" /><p class="wp-caption-text">Pomeriggio d&#39;agosto sull&#39;Altopiano</p></div>
<p>In questi giorni sto finendo di ri-caricare i contenuti persi. Ora, con i backup periodici schedulati e funzionanti, mi sento più tranquillo e sereno.</p>
<h2>Riferimenti</h2>
<p>- lftp: <a href="http://lftp.yar.ru/">http://lftp.yar.ru/</a></p>
<p>- Cpanel: <a href="http://www.cpanel.net/">http://www.cpanel.net/</a></p>
<p>- MysqlAdmin: <a href="http://sourceforge.net/projects/mysql-admin/">http://sourceforge.net/projects/mysql-admin/</a></p>
<p>- Altopiano di Asiago: <a href="http://www.asiago7comuni.to/it/home.htm">http://www.asiago7comuni.to/it/home.htm</a></p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(406, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 0"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_406_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_406_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/406/grazie-google/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Eseguire Stored Procedures in una funzione UDF (SQL Server)</title>
		<link>http://www.maurodalfreddo.it/archives/97/eseguire-stored-procedures-in-una-funzione-udf-sql-server</link>
		<comments>http://www.maurodalfreddo.it/archives/97/eseguire-stored-procedures-in-una-funzione-udf-sql-server#comments</comments>
		<pubDate>Wed, 28 Apr 2010 13:18:07 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Hack]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=97</guid>
		<description><![CDATA[E’possibile eseguire stored procedures in functions UDF con Microsoft SQL SERVER attraverso un workaround. Analisi della problematica e codifica di una .NET external function che permette l’esecuzione di dynamic SQL. (SQL Server: How to execute Stored procedures into a UDF function) <a href="http://www.maurodalfreddo.it/archives/97/eseguire-stored-procedures-in-una-funzione-udf-sql-server">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><em>Un workaround per eseguire stored procedures in functions UDF con Microsoft SQL SERVER. Analizziamo la problematica e scriviamo una .NET external function che permette l’esecuzione di dynamic SQL. </em> </p>
<h2><span style="color: #000000;">Introduzione</span></h2>
<p>Ultimamente mi sono scontrato con la problematica di eseguire una stored procedure da una funzione SQL UDF (<a href="http://www.google.it/search?hl=it&amp;rlz=1G1GGLQ_ITIT252&amp;q=How+to+execute+Stored+procedures+into+a+UDF+function&amp;meta=&amp;aq=f&amp;aqi=&amp;aql=&amp;oq=&amp;gs_rfai=" target="_blank">google</a>: <em>How to execute Stored procedures into a UDF function</em>).<br />
L’esecuzione di codice INSERT/UPDATE/DELETE/STORED PROCEDURE che alterano il contesto esterno alla funzione non è possibile <a href="http://msdn.microsoft.com/en-us/library/ms191320(SQL.90).aspx" target="_blank">by design</a>; è possibile solo apportare modifiche a variabili locali definite nella funzione. Infatti appena tentiamo di alterare il contesto del database otteniamo uno dei seguenti messaggi di errore, rispettivamente se stiamo utilizzando comandi TSQL INSERT/UPDATE/DELETE, chiamando STORED PROCEDURE o cercando di eseguire SQL dinamico:  </p>
<p><span style="color: #ff0000;">Invalid use of side-effecting or time-dependent operator in ‘INSERT’ within a function.</span>  </p>
<p><span style="color: #ff0000;">Only functions and extended stored procedures can be executed from within a function.</span>  </p>
<p><span style="color: #ff0000;">Invalid use of side-effecting or time-dependent operator in ‘EXECUTE STRING’ within a function.</span>  </p>
<p>L’utilizzo delle funzioni SQL è utilizzato per leggere e applicare una certa logica ai dati letti, ma non a trasformarli o modificarli. E’difficile pensare che leggendo dei dati da una tabella con una SELECT questi vengano modificati sotto il naso.<br />
Se ci dovessero essere logiche più complesse, come l’utilizzo di tabelle di appoggio temporanee, certo è preferibile e doveroso racchiudere il codice in una stored procedure.  </p>
<p>Questo in linea di principio e come best practice. </p>
<h2><span style="color: #000000;">Caching con una funzione SQL</span></h2>
<p>Un caso che ho dovuto affrontare era quello dell’ottimizzazione delle performance di una serie di applicazioni. Queste, accedendo al DB, ed utilizzando gli strumenti offerti TSQL, utilizzavano tutte, in più punti e ripetutamente una certa funzione, che, accedendo a dati esterni al DB con protocollo TCP/IP, risultava particolarmente lenta a rispondere. </p>
<p>Da qui l’idea di implementare un <em><strong>meccanismo di caching</strong></em> nella funzione stessa; non era assolutamente pensabile cambiare la logica<br />
applicativa modificando il codice e le modalità di interfacciamento al DB. L’operazione doveva essere completamente trasparente per le applicazioni!  </p>
<p>L’algoritmo di caching può essere banalmente il seguente:  </p>
<pre>IF (prima volta)
 BEGIN
  recupera i dati online
  memorizzali nella cache
 ELSE
  recupera i dati dalla cache
 END</pre>
<p>Includere tale logica in una funzione SQL è semplice…<br />
Peccato che il significato di <em>memorizzare i dati</em> è quello di utilizzare un’istruzione del tipo INSERT/UPDATE/ST.PROCEDURE e che quindi modifica il contesto esterno alla funzione.  </p>
<p>Nel seguito espongo la soluzione di <strong>come eseguire una stored procedure in una funzione</strong>, descrivendo i passi utili a creare una logica di caching sopra descritta.  </p>
<p>Immaginiamo di avere una funzionalità di basso livello (my_very_slow_function) che, dopo essersi connessa con un tal sistema autorizzativo, restituisca i ruoli associati ad un utente.<br />
Vogliamo utilizzare questa funzionalità in una funzione di più alto livello (my_function) che incapsuli la logica (quindi una funzione wrapper)  </p>
<pre>CREATE FUNCTION [dbo].my_very_slow_function(@login nvarchar(50))
RETURNS TABLE
AS
RETURN
(  
 SELECT 'Administrator' as Role,0 as Type UNION
 SELECT 'Contributor', 0 UNION   
 SELECT 'Public role', 0 UNION   
 SELECT @login, 1
)</pre>
<pre>SELECT type,role FROM [dbo].my_very_slow_function('DOMAIN\testuser') ORDER BY type,role
<em>type role</em>
<span style="color: #0000ff;">0    Administrator
0    Contributor
0    Public role
1    DOMAIN\testuser</span></pre>
<pre>CREATE FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 INSERT INTO @roles (role,type)
  SELECT role,type FROM my_very_slow_function(@login)
  RETURN
END</pre>
<pre>SELECT type,role FROM [dbo].my_function('DOMAIN\testuser') ORDER BY type,role</pre>
<p>Vogliamo quindi inserire nella funzione wrapper my_function la logica di caching. Creiamo la tabella per la cache e modifichiamo opportunamente la funzione.  </p>
<pre>
<pre>CREATE TABLE dbo.MYCACHE (
login VARCHAR(50) NOT NULL,
role VARCHAR(400) NOT NULL,
type INT NOT NULL,
ts datetime DEFAULT getdate(),
PRIMARY KEY (login,role)
)</pre>
<pre>GO</pre>
<pre>ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  INSERT INTO [dbo].MYCACHE (login,role,type)
   SELECT @login,role,type FROM my_very_slow_function(@login)
  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END</pre>
</pre>
<p>Se tentiamo di modificare la funzione in tal senso otteniamo il seguente errore:  </p>
<pre><span style="color: #ff0000;">Msg 443, Level 16, State 15, Procedure my_function, Line 11
Invalid use of side-effecting or time-dependent operator in 'INSERT' within a function.</span></pre>
<p>Cerchiamo di seguire un’altra strada: eseguire nella funzione una stored procedure contenente la logica del caching.  </p>
<pre>CREATE PROCEDURE [dbo].MYCACHEINSERT 
 @login VARCHAR(50) 
AS
BEGIN
 INSERT INTO [dbo].MYCACHE (login,role,type)
  select @login,role,type
  FROM dbo.my_very_slow_function(@login) 
END
GO

ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 DECLARE @sql NVARCHAR(400)

 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  exec dbo.MYCACHEINSERT @login

  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END
GO</pre>
<p>Questa volta la creazione/aggiornamento non hanno problemi ma si presenta un errore in fase di esecuzione  </p>
<pre>SELECT type,role FROM [dbo].my_function('DOMAIN\testuser') ORDER BY type,role</pre>
<pre><span style="color: #ff0000;">Msg 557, Level 16, State 2, Line 1
Only functions and extended stored procedures can be executed from within a function.</span></pre>
<p>Possiamo anche provare a modificare la funzione per farle eseguire codice SQL dinamico, e questa volta otteniamo un errore in fase di modifica.  </p>
<pre>ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 DECLARE @sql NVARCHAR(400)

 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  SELECT @sql = 'exec dbo.MYCACHEINSERT '''+@login+''''
  exec(@sql)

  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END</pre>
<pre><span style="color: #ff0000;">Msg 443, Level 16, State 14, Procedure my_function, Line 14
Invalid use of side-effecting or time-dependent operator in 'EXECUTE STRING' within a function.</span></pre>
<p>La spiegazione nella difformità di comportamento dal precedente tentativo sta nel fatto che si può usare EXEC giusto per chiamare Extended Stored Procedures, ovvero le stored procedures scritte in C e compilate. Per definizione non si può cambiare lo stato del Database da dentro la funzione UDF, e così non si può costruire SQL dinamico ed eseguirlo con la stored procedure estesa “sp_executesql” (EXEC).  </p>
<p>Documentandomi in Internet ho trovato un modo per eseguire uno statement SQL di aggiornamento/inserimento usando la funzione OPENQUERY e un LINKED SERVER che si autoreferenzia:  </p>
<pre>SELECT count(*) FROM OPENQUERY(LINKED SERVER,'INSERT INTO ....')</pre>
<p>Peccato che la stringa di testo deve essere una costante, e non è possibile sostituirla con variabili e/o espressioni.  </p>
<h2><span style="color: #000000;">La via programmatica</span></h2>
<p>L’unico modo by design per risolvere il problema è quello di scrivere un’extended stored procedure in C, correndo il rischio che questa non funzioni più nelle prossime versioni di SQL Server, visto che la scrittura di questo tipo di procedures è deprecata. Scrivendo una external stored procedure in C# (.NET) non si risolve il problema, visto che non viene marcata come “extended” da SQL Server e che quindi non è possibile usarla dentro una funzione UDF.  </p>
<p>Anche scrivendo una funzione external in C# ed eseguendo un’operazione INSERT, ottenendo la connessione dal contesto con  </p>
<pre><span style="color: #008000;">SqlConnection conn = new SqlConnection("context connection= true");</span></pre>
<p>otterremmo gli errori iniziali per i quali non è possibile modificare il contesto del DB. L’unico modo alla fine è quello di scrivere una funzione esterna C# ed istanziare una nuova connessione, ma sempre allo stesso database. In tal modo si inganna SQL Server, che non può più riconoscere l’eventuale modifica al contesto esterno.  </p>
<p>Ora apriamo Visual Studio e creiamo un nuovo progetto SQL Server “MYUDF” in C#. Nelle proprietà del progetto ricodiamoci di impostare l’opzione “Sign the assembly”.  </p>
<p>Poi aggiungiamo un nuovo file al progetto dti tipo Funzione UDF:  </p>
<pre><span style="color: #008000;">using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction(Name = "ExecuteSQL", </span></pre>
<pre><span style="color: #008000;">     DataAccess = DataAccessKind.Read, IsDeterministic = false)]</span></pre>
<pre><span style="color: #008000;">    public static int ExecuteSQL(SqlString sql, SqlString connstr)
    {
        using (SqlConnection conn = new SqlConnection(connstr.Value))
        {
            int res=0;
            try
            {                            
                conn.Open();               
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = sql.Value;               
                res = (int)cmd.ExecuteNonQuery();               
            }
            catch (Exception ex)
            {               
                throw;
            }
            finally {                               
                conn.Close();               
            }
            return res;
        }

    }
}</span></pre>
<p>   </p>
<p>La funzione riceve dall’input due parametri: la stringa contenente il codice SQL e quella per la connessione ad un DB. I controlli per la sicurezza e la gestione dell’errore è stata volutamente trascurata per semplificare la lettura. Al build del progetto verrà prodotto l’assembly MYUDF.dll, che dovrà essere copiato sul database server in una certa cartella, diciamo C:\TEMP. Creiamo e carichiamo l’assembly nel DB con il comando TSQL “CREATE ASSEMBLY”:  </p>
<p>L’istruzione CREATE ASSEMBLY MYUDF può fallire e produrre i seguenti messaggi d’errore:  </p>
<p>A) <span style="color: #ff0000;">Msg 10327, Level 14, State 1, Line 1 </span>  </p>
<p><span style="color: #ff0000;">CREATE ASSEMBLY for assembly ‘MYUDF’ failed because assembly ‘MYUDF’ is not authorized for PERMISSION_SET = UNSAFE.  The assembly is authorized when either of the following is true: the database owner (DBO) has UNSAFE ASSEMBLY permission and the database has the TRUSTWORTHY database property on; or the assembly is signed with a certificate or an asymmetric key that has a corresponding login with UNSAFE ASSEMBLY permission.</span>  </p>
<pre>ALTER DATABASE MYDB set trustworthy on;</pre>
<p>B) <span style="color: #ff0000;">Msg 0, Level 20, State 0, Line 0 </span>  </p>
<p><span style="color: #ff0000;">Errore grave durante l’esecuzione del comando corrente. Annullare i risultati eventuali.</span>  </p>
<p>Questo è un BUG noto (almeno per SQL2005), basta eseguire il seguente comando: <em>exec sp_changedbowner ‘&lt;username of the db attacher, ‘sa’, or any windows authenticated login&gt;’</em>  </p>
<p><em> </em>   </p>
<pre>EXEC sp_changedbowner 'sa'

CREATE ASSEMBLY MYUDF
AUTHORIZATION [dbo] FROM 'C:\TEMP\MYUDF.dll' WITH PERMISSION_SET = UNSAFE;

CREATE FUNCTION [dbo].[ExecuteSQL](@sql [nvarchar](4000),@conn [nvarchar](200))
RETURNS int
AS EXTERNAL NAME MYUDF.[UserDefinedFunctions].ExecuteSQL</pre>
<pre>GRANT EXECUTE ON [dbo].[ExecuteSQL] TO [public]</pre>
<p>Abbiamo appena creato la funzione UDF .NET, ed ora ci prepariamo a testarla:  </p>
<pre>select [dbo].[ExecuteSQL]('exec dbo.MYCACHEINSERT ''WORKGROUP\user1''','Data Source=MYSERVER;Initial Catalog=MYDB;Integrated Security=False;user id=dbu;password=dbu;')

<span style="color: #ff0000;">Msg 6522, Level 16, State 1, Line 1
A .NET Framework error occurred during execution of user defined routine or aggregate 'ExecuteSQL':
System.Data.SqlClient.SqlException: EXECUTE permission denied on object 'MYCACHEINSERT', database 'MYDB', schema 'dbo'.</span></pre>
<pre>GRANT EXECUTE ON [dbo].MYCACHEINSERT TO [dbu]</pre>
<p>Ora che abbiamo dato i permessi “rieseguiamo” la funzione, che “magicamente” ritorna i risultati attesi. Infatti nella tabella ci sono 4 righe. Bisogna far notare che è stato creata una login specifica “dbu” per inserire i dati nella tabella di cache e solo per quello scopo. Poi integriamo la chiamata ad ExecuteSQL in my_function.  </p>
<pre>SELECT * FROM dbo.MYCACHE
<span style="color: #0000ff;"><em>login           role            type ts</em>
WORKGROUP\user1 Administrator   0    2010-04-25 12:08:13.513
WORKGROUP\user1 Contributor     0    2010-04-25 12:08:13.513
WORKGROUP\user1 Public role     0    2010-04-25 12:08:13.513
WORKGROUP\user1 WORKGROUP\user1 1    2010-04-25 12:08:13.513</span>
SELECT count(*) FROM dbo.MYCACHE
<span style="color: #0000ff;">4</span>

ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 DECLARE @sql NVARCHAR(400)
 DECLARE @conn NVARCHAR(200)
 DECLARE @count INT

 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  SELECT @sql = 'exec dbo.MYCACHEINSERT '''+@login+'''',
   @conn = 'Data Source=MYSERVER;Initial Catalog=MYDB;Integrated Security=False;user id=dbu;password=dbu;'
  SELECT @count = [dbo].[ExecuteSQL] (@sql,@conn)

  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END

SELECT type,role FROM [dbo].my_function('DOMAIN\testuser') ORDER BY type,role
<em>type role</em>
<span style="color: #0000ff;">0    Administrator
0    Contributor
0    Public role
1    DOMAIN\testuser
</span>
SELECT count(*) FROM dbo.MYCACHE
<span style="color: #0000ff;">8</span></pre>
<div><span style="color: #0000ff;"><span style="color: #000000;">Verifica effettuata e possiamo affermare che… il gioco è fatto! A questo punto abbiamo terminato di implementare il caching che ci prefiggevamo. Infatti le chiamate successive alla prima sono molto più veloci nell’esecuzione. Ora la funzione wrapper my_function può essere utilizzata in altri contesti, in altre funzioni o stored procedures.</span></span></div>
<p><span style="color: #0000ff;"> </span> </p>
<p><span style="color: #0000ff;"> </span> </p>
<p><span style="color: #0000ff;"> </span>  </p>
<div><span style="color: #0000ff;"><span style="color: #000000;">Per esempio:</span>  </span></div>
<div><span style="color: #0000ff;"> </span></div>
<div><span style="color: #0000ff;"> </span></div>
<div><span style="color: #0000ff;"> </span></div>
<div><span style="color: #0000ff;"></span></div>
<p><span style="color: #0000ff;"></p>
<pre><span style="color: #000000;">CREATE FUNCTION [dbo].[my_function2](@login nvarchar(50),@param1 varchar(50))
RETURNS TABLE
AS
RETURN
( 
 SELECT count(*) as N, @param1 as M FROM [dbo].my_function(@login)  
)
SELECT * FROM [dbo].[my_function2]('DOMAIN\test2','param')
<span style="color: #0000ff;"><em>N M</em>
4 param</span></span></pre>
<h2><span style="color: #000000;"> </span></h2>
<h2><span style="color: #000000;">Continuando a sperimentare</span></h2>
<p><span style="color: #000000;">Di seguito espongo una problematica che ho incontrato nell’utilizzare la tecnica sopra esposta in una stored procedure che utilizza un oggetto non locale quale una tabella temporanea (ricordo che è comunque una TABLE nel tempdb). Con simili oggetti si verrebbe a creare una transazione distribuita, ma SQL Server, capendo che la connessione istanziata dal codice .NET, individua un conflitto di sessioni. E da qui il messaggio di errore di cui sotto.</span></p>
<pre><span style="color: #000000;">CREATE PROCEDURE [dbo].[my_procedure]  
 @login varchar(50),
 @param1 varchar(50)
AS
BEGIN 
 SET NOCOUNT ON;
 SELECT count(*) as N, @param1 as M FROM [dbo].my_function(@login)  
END
exec [dbo].[my_procedure] 'DOMAIN\test2','param'
<span style="color: #0000ff;">N M
4 param
</span></span></pre>
<pre><span style="color: #000000;">ALTER PROCEDURE [dbo].[my_procedure]  
 @login varchar(50),
 @param1 varchar(50)
AS
BEGIN 
 SET NOCOUNT ON;
 SELECT count(*) as N, @param1 as M INTO #temp FROM [dbo].my_function(@login)  
 SELECT * FROm #temp
END
GO
exec [dbo].[my_procedure] 'DOMAIN\test5','param'</span></pre>
<p>   </p>
<pre><span style="color: #ff0000;">Msg 6522, Level 16, State 1, Procedure my_procedure, Line 7
A .NET Framework error occurred during execution of user defined routine or aggregate 'ExecuteSQL':
System.Data.SqlClient.SqlException: Transaction context in use by another session.</span></pre>
<p><span style="color: #000000;">La soluzione è più semplice del previsto: poichè SQL Server tenta implicitamente di costruire una transazione distribuita, il workaround è quello di inibire a priori tale possibilità attraverso il parametro opzionale <strong>Enlist</strong> nella connectionstring. E infatti, dopo la modifica della stringa di connessione, l’esecuzione funziona come ci si aspetta, ritornando i valori aspettati.</span></p>
<pre><span style="color: #000000;">ALTER FUNCTION [dbo].[my_function](@login nvarchar(50))
RETURNS
@roles TABLE (role varchar(400), type int)
AS
BEGIN
 DECLARE @sql NVARCHAR(400)
 DECLARE @conn NVARCHAR(200)
 DECLARE @count INT

 INSERT INTO @roles (role,type)
  SELECT role,type FROM dbo.MYCACHE
  WHERE login = @login
 IF @@ROWCOUNT = 0
 BEGIN  
  SELECT @sql = 'exec dbo.MYCACHEINSERT '''+@login+'''',
   @conn = 'Data Source=MYSERVER;Initial Catalog=MYDB;Integrated Security=False;user id=dbu;password=dbu;Enlist=no'
  SELECT @count = [dbo].[ExecuteSQL] (@sql,@conn)

  INSERT INTO @roles (role,type)
   SELECT role,type FROM dbo.MYCACHE
   WHERE login = @login
 END  
  RETURN
END

exec [dbo].[my_procedure] 'DOMAIN\test7','param'</span></pre>
<pre><span style="color: #0000ff;"><em>N M</em>
4 param </span></pre>
<pre><strong> </strong></pre>
<h2><span style="color: #000000;">Conclusioni</span></h2>
<p><span style="color: #000000;">L’utilizzo di stored procedures in funzioni è inibito in SQL Server by design, ma creando una external UDF in C#.NET è possibile aggirare l’ostacolo. E’stato presentato il codice e un esempio di come implementare il caching per una funzione “lenta”. </span><span style="color: #000000;"> </span> </p>
<p><span style="color: #000000;">  </span>  </p>
<p></span></p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(97, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 2"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_97_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_97_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/97/eseguire-stored-procedures-in-una-funzione-udf-sql-server/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Web Application Stress Tool anche su XP</title>
		<link>http://www.maurodalfreddo.it/archives/89/web-application-stress-tool-anche-su-xp</link>
		<comments>http://www.maurodalfreddo.it/archives/89/web-application-stress-tool-anche-su-xp#comments</comments>
		<pubDate>Tue, 15 Sep 2009 13:14:45 +0000</pubDate>
		<dc:creator>Mauro Dalfreddo</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Hack]]></category>
		<category><![CDATA[IIS]]></category>
		<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.maurodalfreddo.it/?p=89</guid>
		<description><![CDATA[Hack: come attivare tutte le funzionalità di Microsoft Web Application Stress Tool su XP e Vista <a href="http://www.maurodalfreddo.it/archives/89/web-application-stress-tool-anche-su-xp">Continua a leggere<span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><em>Un hack : come attivare tutte le funzionalità di WAST su XP e Vista</em></p>
<p><em> </em></p>
<p>Sviluppare ed implementare un sito o un’applicazione web è un processo creativo che può essere più o meno interessante e quindi più o meno divertente…</p>
<p>E quando finisce il divertimento cominciano i grattacapi! Sicuramente qualcuno nel management o il cliente che ha commissionato il progetto o forse noi stessi vorremmo conoscere quantitativamente le sue prestazioni, in termini di tempi di risposta in funzione del numero di utenti.</p>
<p>Infine potremo arrivare a determinare la capacità del sito, ovvero il numero massimo di richieste/sec aventi tempi di risposta decenti (ovvero prima il numero di utenti appena prima del degrado/collasso del sistema)</p>
<p><img class="alignnone size-full wp-image-320" title="Misurare le performance di un sito web" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/grafico.jpg" alt="" width="493" height="202" /></p>
<p>Quindi dopo aver sviluppato e pubblicato un sito o una web application conviene affrontare l’annoso problema dei test di carico e della simulazione realistica delle navigazioni degli utenti per:</p>
<ul>
<li>vedere  come si comporta il sistema</li>
<li> se sussitono colli di bottiglia, aree o sottosistemi dove è possibile ottimizzare qualcosa </li>
<li>dove è meglio iniziare ad effettuare il cosiddetto tuning.</li>
</ul>
<p>Allo scopo si possono trovare vari tools commerciali e opensource.<br />
E’ possibile trovare una lista abbastanza esaustiva dei software all’url <a href="http://www.softwareqatest.com/qatweb1.html" target="_blank">http://www.softwareqatest.com/qatweb1.html</a>.</p>
<p>Nell’ultima esperienza personale, non avendo (mai) molto tempo a disposizione mi sono concentrato su alcuni software grafici e interfacciabili con i sistemi Windows Server:<br />
- l’ottimo ma assi costoso <a href="http://www.neotys.com/" target="_blank">Neoload</a>, che permette di controllare e verificare graficamente  tutte le fasi del processo dal design alla fase di test e l’analisi dei risultati<br />
- gratuitamente in casa Microsoft si trovano WCAT (Web Capacity Analysis Tool), largamente programmabile ma solo tramite script,<br />
e WAST (<a href="http://www.iis.net/downloads/default.aspx?tabid=34&amp;g=6&amp;i=1298" target="_blank">Web Application Stress Tool – Homer </a>), il ben noto ma vetusto tool grafico.</p>
<p>WAST (o WAS) è uno strumento di simulazione e di carico sviluppato e rilasciato molti anni or sono da Microsoft, ma è un piccolo gioiellino, poichè permette di controllare e verificare graficamente tutte le fasi del processodi test:</p>
<ul>
<li>la registrazione visuale della navigazione effettuata con il browser</li>
<li>la creazione dei profili delle pagine di test</li>
<li>la definizione degli utenti</li>
<li>la distribuzione delle tipologie di test</li>
<li>la definizione del pool di macchine che partecipano al test di carico</li>
<li>la definizione dei contatori (performance counter) delle macchine oggetto di test</li>
<li>la gestione del test vero e proprio, ovvero lo stress del sito o dell’applicazione web</li>
<li>la collezione e l’elaborazione dei risultati, anche se in forma testuale.</li>
</ul>
<p>Purtroppo WAST è supportato ufficialmente solo su Microsoft Windows NT 4.0 SP 4+ e Microsoft Windows 2000, sistemi operativi che ormai non esistono (quasi) più negli ambiti di produzione.</p>
<p>L’installazione su Windows XP và praticamente sempre a buon fine, ed eseguendo WAST si possono sperimentare da subito le varie funzionalità tranne due:</p>
<ul>
<li>non funzionano performance counter</li>
<li>non funziona la comunicazione con i client del pool</li>
</ul>
<p>Su Windows Vista invece si ottiene da subito un errore (<a href="http://weblogs.asp.net/steveschofield/archive/2007/03/10/iis7-post-32-web-application-stress-tool-on-vista.aspx" target="_blank">Steve Schofield Weblog – WAST on Vista </a>), ma basta caricare il file msvcp50.DLL in c:\windows\system32 (poiché non è compreso di default nel SO).</p>
<p>Vista l’indubbia utilità dell’applicazione, mi sono intestardito nel far funzionare WAST anche sui SO più recenti…e alla fine, dopo un po’ di analisi e troubleshooting ci sono riuscito.</p>
<p><strong>Performance counters</strong></p>
<p>Per far funzionare la finestra di ricerca e di aggiunta dei performance counters delle macchine remote, nonchè il polling dei valori a runtime, basta copiare nella directory del programma il file <strong>pdh.dll</strong>, che si trova nella distribuzione di Windows 2000, e che è infatti la libreria che contiene le funzioni per i contatori.</p>
<p>Sicuramente WAS è stato compilato nel 2000 utilizzando quella versione di libreria (5.0.2174.1), mentre su XP c’e’ quella nuova (5.1.2600.3536)</p>
<p><a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/wast_xp_perf_counters.jpg"><img class="alignnone size-full wp-image-346" title="WAST XP Performance counter" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/wast_xp_perf_counters.jpg" alt="" width="634" height="378" /></a></p>
<p><strong>Clients</strong></p>
<p>Bisogna tenere presente che WAST è un tool client/server: l’interfaccia client (hclient.exe) colloquia localmente e/o remotamente (con tecnologia DCOM) con un windows service (webtool.exe); inoltre i dati sono resi persistenti su un database access.</p>
<p>Per far funzionare la comunicazione con i client definiti nel pool bisogna quindi  “giocare” con i pemessi DCOM (dcomcnfg.exe).</p>
<p>Si deve ricordare inoltre che, la comunicazione ed in generale i sistemi Microsoft prima del SP2 di Windows XP non erano securizzati di default, e quindi, per far funzionare il nostro WAST su XP, bisogna “rilassare” un pochino la sicurezza.</p>
<p><img class="alignnone size-full wp-image-345" title="WAST Clients" src="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/wast_xp_clients.jpg" alt="" width="549" height="380" /></p>
<p>Procedimento:</p>
<ol>
<li>Eseguire dcomcnfg.exe</li>
<li>Visualizzare le Proprietà di Servizi Componenti – Computer – Risorse Computer</li>
<li>Nel Tab Proprietà predefinite selezionare la casella Abilita servizi Internet COM in questo computer (che di default è deselezionato)</li>
<li>Nel Tab Protezione COM: Modificare i Limiti delle autorizzazioni di accesso abilitare per l’ACCESSO ANONIMO l’accesso remoto;<br />
Modificare i Limiti delle autorizzazioni di esecuzione e attivazione  abilitare per Everyone l’avvio remoto e l’attivazione remota</li>
<li>Entrare nel dettaglio dei componenti DCOM e aprire le Proprietà di “WebTool”: personalizzare eventualmente le ACL in modo tale che Everyone possa avviare ed attivare remotamente il componente</li>
</ol>
<p>E il gioco è fatto! Provare per credere…</p>
<p><strong>Risorse</strong></p>
<p><strong>-</strong> <a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/WAST_XP.zip">Dll aggiuntive per WAST su XP</a></p>
<p>- <a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/web_stress_tool_setup.zip">Web Application Stress Tool </a>(compressed with 7zip)</p>
<p>- <a href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/DCOM-Settings-for-WAST-on-XP.pdf">DCOM Settings for WAST on XP</a></p>
<p><strong> </strong></p>
<p><strong>Riferimenti</strong></p>
<p>- How To Install and Use the Web Application Stress (WAS) Tool</p>
<p><a href="http://support.microsoft.com/kb/313559/en-us" target="_blank">http://support.microsoft.com/kb/313559/en-us</a></p>
<p>- Introducing Microsoft Web Application Stress Tool (BY Jigesh Shah)</p>
<p><a href="http://www1bpt.bridgeport.edu/sed/projects/cs597/Fall_2002/jishah/web_application_stress.htm" target="_blank">http://www1bpt.bridgeport.edu/sed/projects/cs597/Fall_2002/jishah/web_application_stress.htm</a></p>
<p>- Load Testing Web Applications using Microsoft’s Web Application Stress Tool (By Rick Strahl)</p>
<p><a href="http://www.code-magazine.com/article.aspx?quickid=0001091&amp;page=1" target="_blank">http://www.code-magazine.com/article.aspx?quickid=0001091&amp;page=1</a></p>
<p>- Download Microsoft Web Application Stress Tool – Homer:</p>
<p><a href="http://www.iis.net/downloads/default.aspx?tabid=34&amp;g=6&amp;i=1298" target="_blank">http://www.iis.net/downloads/default.aspx?tabid=34&amp;g=6&amp;i=1298</a></p>
<p>- Steve Schofield Weblog – WAST on Vista</p>
<p><a href="http://weblogs.asp.net/steveschofield/archive/2007/03/10/iis7-post-32-web-application-stress-tool-on-vista.aspx" target="_blank">http://weblogs.asp.net/steveschofield/archive/2007/03/10/iis7-post-32-web-application-stress-tool-on-vista.aspx</a></p>
<p>- Recentemente c’e’ stato qualche problema a reperire WAST sul sito Microsoft download, per cui allego il <a title="web_stress_tool_setup" href="http://www.maurodalfreddo.it/wp-content/uploads/2010/09/web_stress_tool_setup.zip">setup</a>, almeno finchè rimarrano tali problemi. ( N.B. nel caso ci fossero problemi di decompressione è stato compresso con 7zip).</p>
<div class="thanks_button_div" style="float: left; margin-right: 10px;"><div style="float: left; display: inline;"><input type="button" onclick="thankYouButtonClick(89, 'Hai già lasciato un &rdquo;Grazie&rdquo; per questo articolo')" value="Grazie, mi piace: 10"
                class="thanks_button thanks_custom_button "
                style="background-image:url(http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/thanks_compact_black1long.png);width:180px; height:26px; font-family: Verdana, Arial, Sans-Serif; font-size: 12px; font-weight: normal;; color:#ffffff;"
                id="thanksButton_89_1" title="Clicca per ringraziare per questo articolo"/></div><div id="ajax_loader_89_1" style="display:inline;visibility: hidden;"><img alt="ajax loader" src="http://www.maurodalfreddo.it/wp-content/plugins/thanks-you-counter-button/images/ajax-loader.gif" /></div></div>]]></content:encoded>
			<wfw:commentRss>http://www.maurodalfreddo.it/archives/89/web-application-stress-tool-anche-su-xp/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

