<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/rss/1.0/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
<channel rdf:about="http://www.4tm.com.ar/4tmsite/es/index.php">
<title>4TM.biz Blog</title>
<link>http://www.4tm.com.ar/4tmsite/es/index.php</link>
<description>4TM.biz Blog</description>
<dc:language>es</dc:language>
<dc:rights>Copyright 2012 4Team, http://www.4tm.com.ar. All Rights Reserved.</dc:rights>
<dc:date>Sat, 19 May 2012 17:50:03 GMT</dc:date>
<dc:publisher>4TM.biz</dc:publisher>
<dc:creator>jose.canciani (at) 4tm.biz</dc:creator>
<dc:subject>Technology</dc:subject>
<syn:updatePeriod>weekly</syn:updatePeriod>
<syn:updateFrequency>1</syn:updateFrequency><items>
 <rdf:Seq>
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=39&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=38&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=37&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=36&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=34&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=31&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=30&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=28&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=27&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=26&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=25&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=24&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=23&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=21&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=20&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=16&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=12&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=11&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=10&amp;set_lang=es" />
  <rdf:li rdf:resource="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=7&amp;set_lang=es" />
 </rdf:Seq>
</items>
</channel>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=39&amp;set_lang=es">
<title><![CDATA[Time Machine with OSX Lion and a Linux server]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=39&amp;set_lang=es</link>
<description><![CDATA[<p>&nbsp;</p>
<p>I got my new Mac Book Air, 13", 2011, Core i7, 256GB SSD. Incredible speed and portability. And the new<strong> Mac OS Lion.</strong><br /><br />Everything worked great except... <strong>Time Machine</strong>. I have a <strong>fitPC1</strong> (<a href="http://www.linuxfordevices.com/c/a/News/Tiniest-Linux-system-yet/">http://www.linuxfordevices.com/c/a/News/Tiniest-Linux-system-yet/</a>) from several years ago that I used as a File Server, SVN repo and LAMP server (among other things like torrent client). It worked great with Snow Leopard, but it was time for an upgrade.<br /><br />Apparently the old version of <strong>NetATalk</strong> I had installed does not worked with Lion anymore. No new packages (of anything) were to be found for my <strong>Ubuntu</strong> 8.10. So I started with the long -but no so painful- upgrade process: 8.10 =&gt; 9.04 =&gt; 9.10 =&gt; 10.04.3 LTS (I could have made a clean install, but didn't want to reconfigure everything).<br /><br />After that, I installed netatalk 2.2 from source (download at <a href="http://netatalk.sourceforge.net/">http://netatalk.sourceforge.net/</a> )</p>
<p>&nbsp;</p>
<pre class="brush: bash">./configure --enable-debian 
make 
make install
</pre>
<p>&nbsp;</p>
<p>Everything default but with the debian keyword to have scripts added in my ubuntu init. Avahi daemon was already setup from my previous install, if you don't have it, see next link).<br /><br />Differently from the binary package I used the first time (as mentioned here: <strong>HowTo: Make Ubuntu A Perfect Mac File Server And Time Machine Volume </strong><a href="http://www.kremalicious.com/2008/06/ubuntu-as-mac-file-server-and-time-machine-volume/">http://www.kremalicious.com/2008/06/ubuntu-as-mac-file-server-and-time-machine-volume/ </a>), configure put my config files in /usr/local/etc/netatalk by default, so I created a symbolic link from<strong> /etc/netatalk =&gt; /usr/local/etc/netatalk </strong>and replaced/updated my old files as I see fit.<strong><br /></strong><br />I tried to <strong>add the uams_dhx2.so to the afpd.conf file</strong>, which was suggested in every forum I visited, with not so much luck. Lion saw the volumes, but kept giving me permissions errors. I had to change from cdb to dbd in the <strong>AppleVolumes.default</strong> file, and deleted all .Apple* from all directories in the shared folders just to be sure there was no conflic with the old db).<br /><br />After that, everything was working but Time Machine. I could not see the drive available in the Time Machine setup screen.<br /><br />I googled and googled, tried several things until I got it working. Let me make a list of what I did:<br /><br /><a href="http://forum.wegotserved.com/index.php/topic/19923-timemachine-on-lion-with-netatalk/">http://forum.wegotserved.com/index.php/topic/19923-timemachine-on-lion-with-netatalk/</a><br />This is a coLinux tutorial (running netatalk on Windows with coLinux). It had useful scripts to configure some files. These one I used:<br /><br /></p>
<pre class="brush: bash">sed -i s/Xserve/TimeCapsule6,106/ /etc/avahi/services/afpd.service
uuid=`uuidgen | tr '[a-z]' '[A-Z]'`;mac=$(ip link show eth0 | awk '/ether/ {print $2}')
echo -e '<!--?xml version="1.0" standalone="no"?-->\n\n\n\t\n\t\t_adisk._tcp\n\t\t9\n\t\tsys=waMA='$mac',adVF=0x100\n\t\tdk0=adVF=0x81,adVN=TimeMachine,adVU='$uuid'\n\t\n' &gt;/etc/avahi/services/adisk.service
mkdir -p /media/tm # for log file
echo - -tcp -noddp -uamlist uams_dhx.so,uams_dhx2.so -nosavepassword -setuplog \"default log_warn /media/tm/.afpd.log\" &gt;&gt;/etc/netatalk/afpd.conf
echo \"TimeMachine\" $uuid &gt;&gt;/etc/netatalk/afp_voluuid.conf
</pre>
<p><br />It still didn't work, but helped me setup several files that didn't exists. There is an "option:tm" in this tutorial that I missed (since I already had my AppleVolumes.default configured and working, so I didn't pay attention to modification of that file). It would have help me later to save some time instead of keep googling.<br /><br />I kept googling and found this one:<strong> Time Machine meet Netatalk</strong> <a href="http://fmepnet.org/time_machine.html">http://fmepnet.org/time_machine.html</a><br />Down there there's a simple touch that did the magic:</p>
<pre class="brush: bash">touch /pool/backup/time_machine/.com.apple.timemachine.supported
</pre>
<p>(change /pool/backup to your time machine shared directory, in my case /mnt/seagate640).<br /><br />Magically the drive appeared in Time Machine, altough not sure if after or before doing this on my macbook air:</p>
<pre class="brush: bash">sudo defaults write com.apple.systempreferences TMShowUnsupportedNetworkVolumes 1
</pre>
<p>I could select the disk, configure Time Machine and start the backup. But, after some seconds, I received this error: <strong>"The network backup disk does not support the required AFP features."</strong><br /><br />I kept googling until I found this newsletter: <a href="http://permalink.gmane.org/gmane.network.netatalk.devel/10018">http://permalink.gmane.org/gmane.network.netatalk.devel/10018</a> that mentioned: <strong>"Please check if you're really setting options:tm for the Time Machine volume"</strong><br /><br />That was it! I modified by AppleVolumes.default:</p>
<pre class="brush: bash">:DEFAULT: options:upriv,usedots
~
/mnt/seagate640 seagate640 allow:joe,melina cnidscheme:dbd options:usedots,upriv,tm
/var/cache/torrentflux torrentflux allow:joe,melina cnidscheme:dbd options:usedots,upriv
</pre>
<p>I added "tm" to my options and it worked! I now have my Lion backing up to my Fit-PC Ubuntu 10.04 server, with a Seagate 640GB Password drive attached.<br /><br />I hope this helps someone to avoid hours googling and trying over and over again :)<br /><br />Regards,<br />Jose</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Sat, 24 Sep 2011 21:03:18 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=38&amp;set_lang=es">
<title><![CDATA[Mysql, SQLite3 and ANSI SQL thoughts]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=38&amp;set_lang=es</link>
<description><![CDATA[<p>I recently did a refactor for one of the tools I maintain to switch from MySQL to SQLite3. It's and error reporter tool, so we need it to be available even if MySQL fails <img title="Tongue out" src="../js/tiny_mce_3.2.7/plugins/emotions/img/smiley-tongue-out.gif" border="0" alt="Tongue out" /><br /> <br /> Even though it's a small app, I hit several things that are worth mentioning. Please note that this is not a recommendation to start doing things differently, it's just a series of issues I found in the process.<br /> <br /> <strong>Strings</strong><br /> <br /> The standard way is to enclose stringsin SQL in simple quotes. No need to escape anything but the simple quote itself. Escaping is done with another simple quote. <br /> <br /></p>
<blockquote><tt>insert into contactInfo (contactId, lastName) values (100, 'O</tt><tt><strong>''</strong></tt><tt>Neal');</tt><br /></blockquote>
<p><br /> Escaping % and _ in the LIKE function is done by adding an explicit the escape character:<br /></p>
<blockquote><tt>select * from contactInfo where firstName like 'hola\_mundo' escape '\';</tt><br /></blockquote>
<p><br /> In Mysql we were using double quotes, and we escape with backslash. This is not cross platform (see ANSI_QUOTES bellow).<br /> <br /> <strong>Object names - identifiers- (tables, columns, functions, etc)</strong><br /> <br /> Almost all database engines are case insensitive when referring to object names. Double quotes is used when you want to use a reserved word, special characters, unicode characters or case sensitivity. If you don't use double quotes, then the names are usually translated to uppercase in the database dictionary, but it remains case insensitive when referring to it. <br /> <br /> In Mysql, table names are case sensitive (because they are files in a case sensitive filesystem, at least on unix/linux). This could be changed using the lower_case_table_names parameter. Mysql would convert all table names to lowercase for storage and lookups.<br /> <br /> But also in MySQL, columns names are not case sensitive. So<br /> &nbsp; select FirstName from contactInfo;<br /> and<br /> &nbsp; select firstname from contactInfo;<br /> is the same, no matter how you write the SQL to create the table.<br /> <br /> In Mysql you can use, like we were doing, backquotes; but that won't help in setting a case to column names (or index, store routines and column aliases)<br /></p>
<blockquote><strong><tt>Mysql</tt></strong><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </tt><tt><strong>Standard SQL</strong></tt><br /> <tt>create table `table` (&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; create table "table" (</tt><br /> <tt>&nbsp; `select` varchar(10)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "select" varchar(10)</tt><br /> <tt>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );</tt><br /></blockquote>
<p><br /> Regarding the use of quotes, we could change the behavior to use the standard doble quotes by setting the ANSI_QUOTES sql mode. Note that once you do change it, all double-quoted strings become identifiers, so all strings are required to be enclosed by single quotes.<br /> <br /> <strong>SQL statement diferences</strong><br /> <br /> INSERT INTO TABLE SET col1 = val1, col2 = val2 is not a valid SQL in other database apart from Mysql. The correct use is INSERT INTO table (&lt;columnlist&gt;) values (&lt;valuelist&gt;), although I have to say that I like this syntax.<br /> <br /> SQLite3 does not support TRUNCATE TABLE, but when doing a DELETE without a where clause, it is treated as a truncate statement.<br /> <br /> MySQL's <br /> &nbsp;&nbsp;&nbsp; "INSERT IGNORE", "INSERT REPLACE", "INSERT ... ON DUPLICATE KEY UPDATE" and "REPLACE", <br /> and SQLite3's <br /> &nbsp;&nbsp;&nbsp; "INSERT OR REPLACE INTO" <br /> are not standard SQL. The correct SQL to do these tasks is the MERGE statement, but non of them supports it (today). It would be a good practice to wrap all this queries using classes, that way time and work for switching dbs -if we need to- will be greatly reduce.<br /> <br /> <strong>SQLite3 Types</strong><br /> <br /> SQLite3 supports less types than mysql:</p>
<blockquote>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLITE3_INTEGER =&gt; 'int',<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLITE3_FLOAT =&gt; 'float',<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLITE3_NULL =&gt; 'null',<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLITE3_TEXT =&gt; 'text',<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SQLITE3_BLOB =&gt; 'blob'<br /></blockquote>
<p>You don't need to specify a character set for a text column, you simply insert what you have in a variable. If its UTF8, you insert UTF8, if its ASCII, you insert ASCII.<br /> <br /> <strong>Bind Variables (or Bound Parameters)</strong><br /> <br /> Both databases now support bind variables. We do not use it today, but when running the same query several times Binding variables have good performance advantages since the query is only parsed once. It also removes the SQL Injection problem, since there is no need to escape strings anymore.<br /> <br /> Here's a quick example, inserting 10000 rows in a table:<br /> <br /> <tt>&nbsp;&nbsp;&nbsp; if ($testType == 'Bind') {</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt = $conn-&gt;prepare("insert into test (a,b,c,d,e,f,g,h,i,j) values (?,?,?,?,?,?,?,?,?,?)");</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for($i=1;$i&lt;=10000;$i++) {</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt-&gt;bind_param("iiiiiiiiii", $i,$i,$i,$i,$i,$i,$i,$i,$i,$i);</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt-&gt;execute();</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</tt><br /> <tt>&nbsp;&nbsp;&nbsp; } else {</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for($i=1;$i&lt;=10000;$i++) {</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt = $conn-&gt;prepare("insert into test (a,b,c,d,e,f,g,h,i,j) values ($i,$i,$i,$i,$i,$i,$i,$i,$i,$i)");</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt-&gt;execute();</tt><br /> <tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</tt><br /> <tt>&nbsp;&nbsp;&nbsp; }</tt><br /> <br /> Results:<br /> Bind 10000 inserts done in 0.57499408721924 seconds<br /> No bind 10000 inserts done in 1.006609916687 seconds</p>
<p>The difference could be even greater on SELECT statements with a lot of tables, since the time to parse and generate an execution plan will increase.</p>
<p><br /> When having hundreds of queries per second, like we do, time and CPU reduction could be considerable. Since MySQL 5.1.17, prepared SQL Statements are cached too.&nbsp;</p>
<p>Regards,</p>
<p>Jose.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Fri, 27 May 2011 17:03:21 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=37&amp;set_lang=es">
<title><![CDATA[Sending HTML AWR reports with unix sendmail]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=37&amp;set_lang=es</link>
<description><![CDATA[<p style="text-align: left;">A small command to send your AWR html report (from sqlplus you run @?/rdbms/admin/awrrpt.sql).</p>
<p style="text-align: left;">&nbsp;</p>
<p style="text-align: left;">Un peque&ntilde;o comando unix para enviar el reporte HTML de AWR (que se obtiene con sqlplus si corres @?/rdbms/admin/awrrpt.sql).</p>
<p>&nbsp;</p>
<pre class="brush: bash">[oracle@DBSERVER]$ cat &lt;&lt; EOF | sendmail -t
&gt; From: oracle@yourserver.yourcompany.com
&gt; To: your.email.address@yourcompany.com
&gt; Mime-Version: 1.0
&gt; Content-type: text/html; charset="iso-8859-1"
&gt; Subject: AWR report inline
&gt; `cat awrrpt_1_10697_10698.html`
&gt; EOF
</pre>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Tue, 17 Aug 2010 20:25:17 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=36&amp;set_lang=es">
<title><![CDATA[Nuevo sitio web / New Website]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=36&amp;set_lang=es</link>
<description><![CDATA[<p>Finalmente se encuentra en linea el nuevo dise&ntilde;o de 4TM.biz. Tambi&eacute;n estrenamos nuevas secciones como Ask A DBA, donde puedes realizar consultas a un DBA Oracle.</p>
<p>Esperamos que te guste el nuevo dise&ntilde;o.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Finally the new website design is online at www.4tm.biz.</p>
<p>We not only changed the design, we are also preseting a new application called Ask A DBA where you can ask question to an Oracle DBA. It's mainly for Spanish use but we will answer in English too if you ask. Remember asktom.oracle.com already does this too!</p>]]></description>
<dc:creator>4tm.biz (4TMbiz at Twitter)</dc:creator><dc:date>Thu, 01 Jul 2010 15:02:36 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=34&amp;set_lang=es">
<title><![CDATA[New Oracle Vulnerabilities]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=34&amp;set_lang=es</link>
<description><![CDATA[<p>These last days have surprised us with several vulnerabilities on Oracle products. Here's a link for an Oracle report from Feb 4th:</p>
<p><a href="http://www.oracle.com/technology/deploy/security/alerts/alert-cve-2010-0073.html">http://www.oracle.com/technology/deploy/security/alerts/alert-cve-2010-0073.html</a></p>
<p>Also it has been circulating a vulnerability that would allow any database user to access any filesystem file with oracle ownership. This is serious since the attacker could potentially delete database files, for example.</p>
<p>Workaround until next patch is easy, just remove execute privilege from PUBLIC to package DBMS_JVM_EXP_PERMS.  Another one includes a DBA escalation (sysdba) via the DBMS_JAVA.SET_OUTPUT_TO_JAVA procedure. You can remove execution on the package, which is not the case for all of you that use the package for something else.</p>
<p>More details on <a href="http://secunia.com/advisories/38353/">http://secunia.com/advisories/38353/</a></p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Mon, 08 Feb 2010 18:54:34 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=31&amp;set_lang=es">
<title><![CDATA[Displaying config for Embedded PLSQL Gateway]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=31&amp;set_lang=es</link>
<description><![CDATA[<p>Recently I posted an article about displaying a binary file from pl/sql directly to a browser. This is done with 10.2's feature EPG (Embedded PL/SQL Gateway). Yes, you can serve HTTP content (html, js, json, binary files like images and of course xml) directly from your Oracle database.   I'll be posting a detailed article about how to configure it (if you need help now, check out this site). For now I'll post a very handy script I've just written.</p>
<p>Sometimes you don't know how EPG is configured and you have to go through the DBMS_EPG package to find that out. It's not a straight thing: you need to do some plsql programming... it would be nice a v$ view but it's not available as far as I know. So this script show come handy for those in need (I know I'll be one of them in the future ;) ).</p>
<p>&nbsp;</p>
<pre class="brush: sql">set serveroutput on

declare
	/**
	*  Get all the database DADs and their configuration
	*  Written by jose.canciani
	*  Source: http://www.4tm.com.ar/4tmsite/wordpress/?p=31
	*/
	dads dbms_epg.varchar2_table;
	mappings dbms_epg.varchar2_table;
	attr_names dbms_epg.varchar2_table;
	attr_values dbms_epg.varchar2_table;
begin
	dbms_output.put_line('-Global Attributes:');
	dbms_epg.get_all_global_attributes(attr_names,attr_values);
	if attr_names.count &gt; 0 then
		for i in 1..attr_names.count
		loop
			dbms_output.put_line('-  '||attr_names(i)||': "'||attr_values(i)||'"');
		end loop;
	else
		dbms_output.put_line('-  Not global attributes found.');
	end if;
	dbms_output.new_line;
	dbms_epg.get_dad_list(dads);
	for i in 1..dads.count
	loop
		dbms_output.put_line('-DAD: "'||dads(i)||'"');
		dbms_epg.get_all_dad_mappings(dads(i),mappings);
		dbms_output.put('-  Mappings:');
		for j in 1..mappings.count
		loop
			dbms_output.put(' "'||mappings(j)||'"');
		end loop;
		dbms_output.new_line;
		dbms_output.put('-  Authorized Users:');
		for j in (select username from dba_epg_dad_authorization where dad_name = dads(i))
		loop
			dbms_output.put(' "'||j.username||'"');
		end loop;
		dbms_output.new_line;
		dbms_output.put_line('-  Attributes:');
		dbms_epg.get_all_dad_attributes(dads(i),attr_names,attr_values);
		for j in 1..attr_names.count
		loop
			dbms_output.put_line('-    '||attr_names(j)||': "'||attr_values(j)||'"');
		end loop;
	end loop;
end;
/
</pre>
<p>&nbsp;</p>
<p>Here's a quick output of the script (I only have one DAD configured and no global attributes):</p>
<pre class="brush: plain">-Global Attributes:
-  Not global attributes found.
-DAD: "ORAMON"
-  Mappings: "/oramon/*"
-  Authorized Users: "ORAMON"
-  Attributes:
-    database-username: "ORAMON"
-    default-page: "showpage?p_name=index.html"
-    error-style: "DebugStyle"
-    owa-debug-enable: "On"
</pre>
<p>That's all!</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Sat, 13 Jun 2009 03:23:55 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=30&amp;set_lang=es">
<title><![CDATA[Facebook hack to see any photo album]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=30&amp;set_lang=es</link>
<description><![CDATA[<p><strong>UPDATE 16-Jul-2009: apparently it's not working anymore! It took several weeks for facebook to fix it! </strong></p>
<p><strong><br /></strong></p>
<p>In order for Facebook to patch this, I'm writing this method that has become so popular these last days.  You can see any album of any user in Facebook by following the next steps:</p>
<p>&nbsp;</p>
<p>1) Get the person's ID: search him/her on facebook and look in the "View Frinds" link where you'll see something like this: http://www.facebook.com/friends/?id=1660869234. This number is the user's ID.</p>
<p>2) Go to http://developers.facebook.com/tools.php?api -&gt; API Test Console</p>
<p>3) Choose "Facebook PHP Client" in Response Format.</p>
<p>4) Choose "fql.query" in Method.</p>
<p>5) Now you'll see a text area called "query": wrote there the following replacing xxxxxxxxxx with the contact's ID you find in the first step:</p>
<p>&nbsp;</p>
<pre class="brush: sql">SELECT name, link FROM album WHERE owner=xxxxxxxxxx</pre>
<p>&nbsp;</p>
<p>6) Run the query to find all the contact's albums. Just take the URL of the album you want and paste it in your browser.</p>
<p>&nbsp;</p>
<p>That's it! Let's hope Facebook fix it soon, but until then, enjoy looking at pictures your enemies shouldn't have uploaded ;)</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Wed, 27 May 2009 13:47:57 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=28&amp;set_lang=es">
<title><![CDATA[Display a BLOB file using PLSQL Web Toolkit]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=28&amp;set_lang=es</link>
<description><![CDATA[<p>I've been working in a simple reporting tool with PL/SQL Web Toolkit in Oracle 10g. Basically you just configure a DAD and the listener opens an HTTP port (8080 by default) and you just access your procedures and print html from PL/SQL. Pretty cool and no extra software or server configuration (no Apache or iAS or APEX needed).</p>
<p>For static content I needed to display image files, so I started looking a bit on Google and I end up with this configuration.</p>
<p>First, I created two tables: one to store my character lob content (CLOB) like html and js files, and another for my binary (BLOB content like images and other binary documents.</p>
<p>&nbsp;</p>
<pre class="brush: sql">create table static_cfiles (
	file_name varchar2(50),
	file_type varchar2(20),
	contents clob,
	primary key (file_name)
);

create table static_bfiles (
	file_name varchar2(50),
	file_type varchar2(20),
	contents blob,
	primary key (file_name)
);
</pre>
<p>&nbsp;</p>
<p>Then, I develop a procedure to read the tables based on the page name parameter. The CLOB is easy to read, you just assign it to a CLOB variable on PL/SQL and print it with HTP.P. But the BLOBS are a bit different. Here's a sample code for getting the blobs:</p>
<p>&nbsp;</p>
<pre class="brush: sql">create or replace procedure showblob (p_name IN VARCHAR2)
as
	page_b_contents blob;
	intImgSize INTEGER;
	content_type static_bfiles.file_type%TYPE;
begin
	
	select file_type, contents
	into content_type, page_b_contents
	from static_bfiles
	where file_name = p_name;
		
	-- get image size
	intImgSize := dbms_lob.getlength(page_b_contents);
	
	-- print the header    	
	owa_util.mime_header(content_type, FALSE, NULL);
    	htp.p('Content-length: '|| intImgSize);
    	-- htp.p('Pragma: no-cache');
    	-- htp.p('Cache-Control: no-cache');
    	-- htp.p('Expires: Thu, 01 Jan 1970 12:00:00 GMT');
    	owa_util.http_header_close;

 	-- download BLOB
	wpg_docload.download_file(page_b_contents);
	
end;
/
</pre>
<p>&nbsp;</p>
<p>The wpg_docload.download_file procedure does all the magic: it signals the PL/SQL Gateway that the blob is to be downloaded to the client's browser. No copy is needed!</p>
<p>I've seen examples that do a dbms_lob.copy to a PL/SQL variable previously to displaying the file: that's dangerous! Imagine you have a 4GB document... you'll be eating all the PGA!</p>
<p>The Content-Length is useful when you are sending a big file since your browser will be able to know how much is left and can draw you a progress bar or just give you the percent done. I've also put there other common headers, feel free to use them as you need.</p>
<p>For static images you better not use the No-Cache option... unless you are changing images from time to time :)</p>
<p>Jose.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Tue, 12 May 2009 18:56:42 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=27&amp;set_lang=es">
<title><![CDATA[Oracle: Histograms and Bind ]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=27&amp;set_lang=es</link>
<description><![CDATA[<p>When the Oracle Optimizer needs to generate an execution plan for a query it goes to through the already taken statistics for the tables and indexes involved.</p>
<p>Histograms are one of those statistics taken from tables. Basically they show how the values of a column are distributed. Oracle will use that to determine how many number of rows will be return by a certain filter on the column. If there are few rows, then it will choose to use an index (if any on that column). If there are a lot of rows then Oracle could choose to do a full table scan.</p>
<p>Doing these calculations requires resources (CPU) so we use Bind Variables to avoid re-parsing of the SQL. So once the plan has been calculated, Oracle will use it again and again until it is removed from the shared pool. The execution plan is calculated only once. But when using Bind Variables, what values are used to calculate the plan? Yes, the values of the first query it processes.</p>
<p>Do you start to see the problem here?</p>
<p>Lets say your table has a column called callerid. This column stores the caller phone number, and "0" if no callerid was registered. Now let's say you store millions of calls per month. Your table is now 60 million rows.</p>
<p>Each phone number does an average of 5 calls per month. Top call registered from one number is 2000. Those are very good numbers for creating a btree-index on "callerid".</p>
<p>But what happens with the "0" value? You'll have million of rows and the index is not a good choice (since it will be cheaper to do a full scan than to use the index with so many rows).</p>
<p>That's exactly the problem: if the first query Oracle recieves has the "0" value in the callerid column, then the stored plan will not use an index, and all future queries will be slow even with different values on callerid!!!</p>
<p>Let's get the histogram information:</p>
<p>&nbsp;</p>
<pre class="brush: sql">select num_rows from dba_tables where owner = 'OWNER' and table_name = 'CALLS';

  NUM_ROWS
----------
  63627796


select HISTOGRAM from dba_tab_col_statistics where owner = 'OWNER' and table_name = 'CALLS' and column_name = 'CALLERID';

HISTOGRAM
---------------
HEIGHT BALANCED


select callerid, count(1) 
from calls
group by callerid 
order by 2 desc

CALLERID          COUNT(1)      
--------------------------
0                 3127825 
310596574            1808 
293584543            1405          
915089527            1229          
915144244             694 
...
</pre>
<p>&nbsp;</p>
<p>And now let's take a look at the histograms and see how even is this data distributed on the table:</p>
<p>&nbsp;</p>
<pre class="brush: sql">SQL&gt; select ENDPOINT_NUMBER, ENDPOINT_VALUE
  2  from dba_histograms
  3  where table_name = 'CALLS' and column_name = 'CALLERID' and owner = 'OWNER'
  4  order by 1;
 
ENDPOINT_NUMBER ENDPOINT_VALUE
--------------- --------------
              8              0
              9      269304330
             10      283241184
             11      289850129
             12      295923724
             13      296092273
             14      296248610
             15      296416759
             16      296585656
             17      296757192
...
</pre>
<p>&nbsp;</p>
<p>As you can see the table is HEIGHT BALANCED according to the statistics taken previously, that is their values are evenly distributed, except for the first 8 buckets of the histogram. Each bucket in the histogram represents (63627796/254) ~= 250503 rows. So in our case the "0" value is repeated (8*250503)~= 2 million rows.</p>
<p>Remember we said that the Optimizer builds the execution path based on the values of the first query it receives? What happens if the first query it receives involves a CALLERID = 0?</p>
<p>&nbsp;</p>
<pre class="brush: sql">select * 
from calls 
where callerid = :b1 
</pre>
<p>&nbsp;</p>
<p>:b1 bind variable is 0, so oracle receives the query and determine that it does not need to use the index on callerid. This is the best thing to do with a "0" value, but next time the application will do the same select with callerid = 269304330 and what will happen? Since the execution plan is already on the Shared Pool, it will be reused, WITHOUT THE INDEX!!!!</p>
<p>This is called "Variable Peeking", and is a know problem. It can happen at any time. Imagine your gather_stats_job run on the CALLS table at Sunday night. The execution plan becomes invalidated. On Monday a user opens the application and do the query with callerid = 0 and BAM! Your new execution plan is created without getting the index.</p>
<p>For this situation you can do several options. The best thing would be to HINT your SQL to use the right index (or not to use it when the value is 0, if you can know it before running it).</p>
<p>But sometimes you can't touch the application code. In those cases you can create a Store Outline for the query with the DBMS_OUTLINE package. See Metalink note 463288.1 for more details.</p>
<p>Jose</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Wed, 15 Oct 2008 21:57:55 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=26&amp;set_lang=es">
<title><![CDATA[Creating PRIVATE DB Links without ownership]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=26&amp;set_lang=es</link>
<description><![CDATA[<p>DBAs usually don't have access to schema passwords. We can only see an encrypted version and that's the way it should be. But from time to time we are asked to create a Database Link. Database Links should never be public because other users in the database can also have access to it, and by extension, to all objects in the remote database.</p>
<p>Database Links cannot be created in other user's schema, even if you are a DBA. For example, a "create database link [schemauser].[dblink_name] ...." will give you an error. So usually what DBAs do is finding out the schema password (which in most cases should not have) or worst, resetting it. Resetting the password is not a bad idea, since you can return to the old password if you save the hash of the original value and then issue an "alter user [schemauser] identified by values '[hash_value]';". But this has a problem: when you are in a live environment you cannot change a password without interrupting sessions, and this is unacceptable even if it is for only a few seconds.</p>
<p>In this cases I like a cleaner approach: create a package in the schema user that will later create the database link using dynamic SQL. Let's go to the example.</p>
<p>(use Sqlplus, or adapt it for your client)</p>
<p>First let's grant a create database link (a not very common privilege that is not usually granted to typical users). Then we create the procedure that will do the "magic" (replace OWNER_NAME for the future owner of the dblink):</p>
<p>&nbsp;</p>
<pre class="brush: sql">grant create database link to OWNER_NAME;  

create or replace procedure OWNER_NAME.e(p_sql varchar2)
is
begin
    execute immediate p_sql;
end;
/  
</pre>
<p>&nbsp;</p>
<p>Now lets create the database link. Replace LINK_NAME, USER_NAME, USER_PASSWORD and TNS_ENTRY for the ones that apply in your case:</p>
<p>&nbsp;</p>
<pre class="brush: sql">exec OWNER_NAME.e('CREATE DATABASE LINK "LINK_NAME"  CONNECT TO  "USER_NAME" IDENTIFIED BY "USER_PASSWORD"  USING ''TNS_ENTRY''');
</pre>
<p>&nbsp;</p>
<p>That should have created the DB link. We can create a second proc to validate if it's working:</p>
<p>&nbsp;</p>
<pre class="brush: sql">reate or replace procedure OWNER_NAME.ee is
v_pp varchar2(30);
begin
   select 1 into v_pp from dual@link_name;
   dbms_output.put_line('pp = '||v_pp);
end;
/
 
set serveroutput on
 
exec OWNER_NAME.ee
</pre>
<p>&nbsp;</p>
<p>You should see "pp=1" when you run the "ee" proc. If you have any error, then work it out! Usually TNS errors are the typical ones (verify you have the appropriate tns entry in your server's tnsnames.ora file).  Finally let's clean all:</p>
<p>&nbsp;</p>
<pre class="brush: sql">revoke create database link from OWNER_NAME;

drop procedure OWNER_NAME.e;

drop procedure OWNER_NAME.ee;
</pre>
<p>&nbsp;</p>
<p>That's all!</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Fri, 26 Sep 2008 21:24:51 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=25&amp;set_lang=es">
<title><![CDATA["NOT IN" vs "NOT EXISTS", the NULL trap]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=25&amp;set_lang=es</link>
<description><![CDATA[<p>Being a DBA, this is a recurrent issue I have every several months with developers: Why my "not in" query is not returning any rows?</p>
<p>One query, two tables:</p>
<p>&nbsp;</p>
<pre class="brush: sql">select count(1) 
from first_table 
where my_row NOT IN (select my_row from second_table);
</pre>
<p>&nbsp;</p>
<p>There's two downsides in using an inner query (subquery) with a NOT IN. The first one is performance: "not in" does not scale well when the second_table is big. You are forcing Oracle to do a full scan on the table, and then compare my_row with the list of values. This is not good.</p>
<p>The second issue is NULL. If my_row is Nullable, and in fact there are null values in some rows of the second_table, then the NOT IN will always give you "TRUE". This is because a NULL value compared with a NOT NULL value always returns FALSE, and since you are using NOT, then FALSE becames TRUE and even if there is no match you will return the row at the end. A quick workaround is to add "where my_row is not null" in the subquery, but the first issue will still be there.</p>
<p>Both issues are solved using NOT EXISTS:</p>
<p>&nbsp;</p>
<pre class="brush: sql">select count(1) 
from first_table t1 
where NOT EXISTS (select 1 
                  from second_table t2 
                  where t2.my_row = t1.my_row);
</pre>
<p>&nbsp;</p>
<p>If you have an index on my_row this will scale perfectly no matter how big is your table.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Thu, 25 Sep 2008 19:21:03 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=24&amp;set_lang=es">
<title><![CDATA[Enable Screen Sharing from shell in Mac OS X]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=24&amp;set_lang=es</link>
<description><![CDATA[<p>Just a quick tip: you are out of your home, need to access your mac desktop, you can login through ssh but Screen Sharing (VNC) is not open.</p>
<p>Simply login as root or admin user and run:</p>
<pre>echo -n enabled &gt; /Library/Preferences/com.apple.ScreenSharing.launchd</pre>
<p>&nbsp;</p>
<p>Now, what if you are connecting from windows with a low bandwidth connection? Here are some tips to run before connecting:</p>
<p style="padding-left: 30px;">1. Enable dock auto hide (useful when you have dynamic icons like CPU usage or bittorrent metrics):</p>
<pre style="padding-left: 30px;">defaults write  com.apple.dock autohide 1 </pre>
<ol style="padding-left: 30px;"> </ol>
<p style="padding-left: 30px;">2. Disable desktop background, set a simple plain color</p>
<p>&nbsp;</p>
<p>Not sure how to do this last one over a ssh shell. Auto hide should be ok, maybe you'll need to kill the dock process to use the new value... Any tips you would like to add will be appreciate, at least until Apple release a viewer for windows that let us tweak connection settings for low bandwith conditions.</p>
<p>Cheers!</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Fri, 11 Jul 2008 16:18:34 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=23&amp;set_lang=es">
<title><![CDATA[Oracle: HOWTO collect session history metrics]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=23&amp;set_lang=es</link>
<description><![CDATA[<p><strong>Update: <span class="comment_text"> Unfourtunatly there's not way to collect  session metrics on AWR (at least not all of the statistics). Oracle  just gave me links to OEM documentation. </span></strong></p>
<p>I've recently had an issue on a database: too much redo is being generated (around 1.5TB a week). It happened from one month to another: we were around 150GB a day and now we are about 300GB a day. Double!</p>
<p>So I was asked to find out what sessions are generating redo. Since I use 10g I immediately thought about AWR. I found DBA_HIST_SESSMETRIC_HISTORY which should have metrics of past sessions. I query the view but no rows were found. I tried to find information on Metalink and nothing. A couple of comments about the view, but no useful information. I opened a TAR (still open while writing, SR 6895109.994) and Oracle Support suggested to enable the Metric in the Database Console. I set a small value ("Redo Writes (per second)" &gt; 0) but nothing: the view was still empty.</p>
<p>Apparently this metric is collected only for the instance, not at a session level. Oracle support is still trying to find information on this to foward, but in the mean time I developed a script to collect any statistic from v$sesstat into my "own private-AWR" views.</p>
<p>I started creating the tables:</p>
<p>&nbsp;</p>
<pre class="brush: sql">-- drop table system.dba_session_sql;
-- drop table system.dba_stats_to_collect;
-- drop table system.dba_session_stat_history;
-- drop table system.dba_sessions_history;
 
create table system.dba_sessions_history (
  id number primary key,
  sid number,
  serial# number,
  logon_time date,
  username varchar2(30),
  program VARCHAR2(48),
  module VARCHAR2(48),
  osuser VARCHAR2(30),
  machine VARCHAR2(64)
) tablespace sysaux;
 
create index system.dba_sess_his_uk_idx on system.dba_sessions_history (logon_time,serial#,sid) tablespace sysaux;
 
create table system.dba_session_stat_history (
  id_session_history number,
  statistic# number,
  value number,
  constraint dba_sess_stat_his_pk primary key (id_session_history,statistic#),
  constraint dba_sess_stat_his_id_fk foreign key (id_session_history) references system.dba_sessions_history (id)
) tablespace sysaux;
 
create table system.dba_stats_to_collect (
  statistic# number primary key,
  min_value number,
  enabled varchar2(1)
) tablespace sysaux;
 
create table system.dba_session_sql_history (
  id_session_history number,
  sql_address raw(8),
  sql_hash_value number,
  sql_text varchar2(1000),
  primary key (id_session_history,sql_address,sql_hash_value),
  constraint dba_sess_sql_id_fk foreign key (id_session_history) references system.dba_sessions_history (id)
) tablespace sysaux;
 
--example, collect "redo size" &gt; 100Mb
insert into system.dba_stats_to_collect values(134,100*1024*1024,'Y');
commit;
</pre>
<p>&nbsp;</p>
<p>This created several tables:</p>
<ul>
<li>dba_sessions_history: main table were the script will save session data (mainly from v$session). </li>
<li>dba_session_stat_history: table were the scripts saves the current (cumulative) value of the statistic# that session is doing. </li>
<li>dba_stats_to_collect: here we store what statistics we would like to collect for the sessions, and the minimal value it must have to start collecting. </li>
<li>dba_session_sql_history: here we store the SQL that the session was running when the collect script run. It does not store ALL sqls for the session, think of it about a hint to identify what was doing (sometimes it's difficult to know which script do what by looking just at the v$session data). </li>
</ul>
<p>After table creation we can insert a new statistic# to collect: in our case statistic# 134 (redo size), when the session is over 100Mb (100*1024*1024).</p>
<p>Now the script:</p>
<p>&nbsp;</p>
<pre class="brush: sql">CREATE OR REPLACE  PACKAGE "SYSTEM"."DBA_SCRIPTS"  as 
 
 /**
 * Collect user-defined statistics every run. Should be in schedule job or cron.
 * Usage:
 * Session info is saved in dba_sessions_history. 
 * Statistic values for the sessions are saved in dba_session_stat_history.
 * Some of the sessions SQLs are saved on dba_session_sql.
 * Statistics to collect are defined in dba_stats_to_collect.
 **/
 procedure collect_sess_stat;
  
end;
/
 
CREATE OR REPLACE  PACKAGE BODY "SYSTEM"."DBA_SCRIPTS"  as
  procedure collect_sess_stat
  is
    v_id number;
    cursor new_sessions is
    select distinct s.sid, serial#, logon_time, username, program,module,osuser,machine
    from v$session s, v$sesstat st, system.dba_stats_to_collect sc
    where s.sid = st.sid
    and sc.statistic# = st.statistic#
    and st.value &gt;= sc.min_value
    and s.username is not null
    and not exists (select 1 from system.dba_sessions_history where sid = s.sid and serial# = s.serial# and logon_time = s.logon_time);
  begin
    v_id := to_char(sysdate,'yyyymmddhh24miss');
    
    for i in new_sessions loop
        insert into system.dba_sessions_history 
        (id,sid,serial#,logon_time,username,program,module,osuser,machine)
    values
        (to_number(v_id||'0'||i.sid||'0'||i.serial#),i.sid,i.serial#,i.logon_time,i.username,i.program,i.module,i.osuser,i.machine);
    end loop;
 
    merge into system.dba_session_stat_history ssh
    using ( select sh.id, st.statistic#, st.value
        from v$session s, v$sesstat st, system.dba_stats_to_collect sc, system.dba_sessions_history sh
        where s.sid = st.sid
        and sc.statistic# = st.statistic#
        and st.value &gt;= sc.min_value
        and s.username is not null
        and sh.sid = s.sid 
        and sh.serial# = s.serial# 
        and sh.logon_time = s.logon_time) scs
    on (scs.id = ssh.id_session_history and scs.statistic# = ssh.statistic#)
    when matched then 
      update set ssh.value = scs.value
    when not matched then
      insert (id_session_history,statistic#,value)
      values (scs.id, scs.statistic#,scs.value);
    
    merge into system.dba_session_sql_history ss
        using ( select sh.id, sql.address, sql.hash_value, sql.sql_text
        from v$sql sql, v$session s, system.dba_sessions_history sh
        where s.sql_address = sql.address
        and s.sql_hash_value = sql.hash_value
        and sh.sid = s.sid 
        and sh.serial# = s.serial# 
        and sh.logon_time = s.logon_time) sqls
    on (ss.id_session_history = sqls.id and ss.sql_address = sqls.address and ss.sql_hash_value = sqls.hash_value)
    when not matched then
      insert (id_session_history,sql_address,sql_hash_value,sql_text)
      values (sqls.id, sqls.address, sqls.hash_value, sqls.sql_text);
 
    commit;
 
  end collect_sess_stat;
 
end;
/
</pre>
<p>&nbsp;</p>
<p>That's it. Just crontab the execution of the collect_sess_stat procedure every five minutes. Then, to view the results, you can query the tables like this:</p>
<p>&nbsp;</p>
<pre class="brush: sql">select sid, serial#, logon_time, username, program, module, osuser_machine, stat_name,
       ceil(stat_value/1024/1024/1024) stat_value_GB, ss.sql_text 
from   system.dba_hist_sess_stat  st,  system.dba_hist_sess_sql ss 
where  ss.id_session_history  = st.id 
order by stat_value desc; 
</pre>
<p>&nbsp;</p>
<p>If I received good input from Oracle Support I'll update this post. In the mean time I'm using this script and it proved to be a very good tool to "point" fingers at culprit sessions.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Mon, 09 Jun 2008 16:36:04 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=21&amp;set_lang=es">
<title><![CDATA[Using UNICODE on web pages]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=21&amp;set_lang=es</link>
<description><![CDATA[<p>For a couple of years 4TM have been developing web pages in UTF-8. Nowadays every modern browser knows how to handle UNICODE charsets and the advantages are big when we have visitors from all over the world.</p>
<p style="text-align: center;"><img title="Unicode  Usage" src="http://www.4tm.com.ar/4tmsite/images/blog/0/0/0/0/0/0/0/0/2/1/Unicode2.gif" alt="Unicode Usage" width="286" height="301" /></p>
<p>UNICODE charsets are becoming very popular these days and it was not a surprise that -according to Google's indexes- this year 2008 the UNICODE charsets are the most used charsets for web pages, surpassing ASCII and Western Europe (ascii + accented characters) charsets.&nbsp;</p>
<p>Source: <a title="Unicode Usage, article source" href="http://googleblog.blogspot.com/2008/05/moving-to-unicode-51.html">http://googleblog.blogspot.com/2008/05/moving-to-unicode-51.html</a></p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Wed, 07 May 2008 20:28:14 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=20&amp;set_lang=es">
<title><![CDATA[A Blogger.com SPAM technique?]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=20&amp;set_lang=es</link>
<description><![CDATA[<p><strong>UPDATE: Apparently my ticket was resolved! Thanks Google Team! For a  non-paid service it was a really quick response (just a couple of days).</strong></p>
<p>4TM has a blog on Blogger.com, <a title="OraAlertCheck Blog" href="http://oraalertcheck.blogspot.com">oraalertcheck.blogspot.com</a>. Recently the blog was taken offline by Google stuff saying that the blog was a SPAM site. Apparently someone/something hit the Flag button several times.</p>
<p>When I enter the site there was a message with a link to the Blogger support site. I opened a new ticket so that they will reopen the blog. Some days later I got my blog back, but with a different URL!!! What a surprise to see that the original URL was taked by a SPAMMER and I can not use my URL again! My new url is oraalertcheck5.blogspot.com (5? does that means there are 4 other spam sites?), so anyone entering my site will think it's no longer active.</p>
<p>I've opened a new ticket to support, and I'm expecting now to obtain the original URL again. I'll keep you posted.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Mon, 27 Aug 2007 16:43:21 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=16&amp;set_lang=es">
<title><![CDATA[The power of Web 2.0, an Advertiser dream]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=16&amp;set_lang=es</link>
<description><![CDATA[<p>I was browsing the internet when I found this curious link: <a href="http://www.lost.eu/4eea0">www.lost.eu/4eea0</a>. Being a <a href="http://www.lostpedia.com">Lost</a> fan I didn't hesitate to click it. To my surprise (?) it has nothing to do with the TV show, but I couldn't resists to enter. Basically it's a student project (that's what the page says); they call it a game.</p>
<p>Some people would say it's not a game if it's not fun to play. And this could be true for most of mankind but the rest it's trapped in Lost mysteries.  The game consists in getting other persons invited. You have a 30 day countdown and every time you invite a person the clock resets to 30 days again. If it reaches 0 you lose. When the page gets 7 million players the game ends. The player who survived with most invites wins.</p>
<p>When you are invited you get your invite code, something like www.lost.eu/4eea0. Every time a person uses that address to enter the game, your clock resets. It's said these addresses are found everywhere, not only on the internet but in train stations walls, bathrooms, posters, everywhere. <em>Relation between the Internet and reality is something that has not exploded yet, and projects like this one shows we are only seeing the top of the iceberg of the "social" web</em>.</p>
<p>When we talk about Web 2.0 we usually get lost explaining how AJAX works. But the social aspect of the concept is probably the most important one.  The game's founder did his homework and a bit of <a title="Social Engineering in Wikipedia" href="http://en.wikipedia.org/wiki/Social_engineering_(computer_security)">Social engineering</a> too: I don't think using Lost as the name of the game is a random event. Lost its probably one of the most spread and well known tv show of the last years. In every corner of the world people has their own theories and want to talk about them. Every new viewer can't wait to see the next chapter. The game certainly fool me, <em>I clicked the link without hesitation</em>.</p>
<p>The web page is simple and it uses Google Maps to locate every user. It's a cool thing to see how a World map is being fill of dots identifying each player.</p>
<p>This social phenomenon is not ignored by marketing and advertising campaigns. I've seen a lot of publicity aimed to make the user invite his friends to do something on the site. Some of them are well conceived: for example Fox LatinAmerica is showing a Simpsons campaign where the concept is that "The Simpsons is more than a show, it's a religion". So it encourages you to build your own church and invite your friends to be part of it. The idea is pretty good (we are really big fans of The Simpsons show over here). But last time I checked there were not many churches loaded...</p>
<p>Examples like this shows us that knowing what the masses want is not an exact science. Viral campaigns, so in fashion today, are created by big companies trying to imitate how a <a href="http://www.youtube.com/watch?v=pv5zWaTEVkI">small and original piece</a> can spread out like cancer. But reality shows they have poor changes of being accepted. There are thousands youtube videos out there that are more original, funny or beautiful than almost anything an Advertising firm can think of.</p>
<p>Buts there's also proven success and people learn from previous experiences. Sometimes repeating the same model of a successful endeavor can repeat it's success. Remember <a title="The Million Dollar Page" href="http://www.milliondollarhomepage.com/">the millon pixels (dollars) homepage</a>? Coca-Cola Mexico has launched a campaign called "<a href="http://futbol.coca-cola.com.mx/camiseta/">La camiseta eres tu</a>" (The national t-shirt is YOU) where every Mexican can support it's national team by creating a big T-shirt with photos of all of them.    Although the site is not very well designed (very heavy to navigate, pretty slow even for broadband) the idea is pretty cool. Some might consider it's a copy of the Millon Dollar Homepage, I believe it's a good twist and use of the social phenomenon in the web 2.0.  But ey! This is a technical weblog, not an advertising one! ;)</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Fri, 01 Jun 2007 19:33:22 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=12&amp;set_lang=es">
<title><![CDATA[UTF-8 PHP obfuscation with Javascript]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=12&amp;set_lang=es</link>
<description><![CDATA[<p>My goal was to write text in a way that bots cannot read it when shown in a HTML page. Javascript should do.</p>
<p>First thing I've tried was to find a javascript-like decodeURI function in PHP which would have made this work very easy, but the only one I could find (<a href="http://www.captain.at">www.captain.at</a>), was not working properly with UTF-8 strings. So I decide to write a very simple one my own.</p>
<p>It's simple the document.write function writing parts of the text. For UTF-8 to work I had to find a function to replace "substr" becouse it does not play well with UTF-8 strings. From the php.net user manual I've found this one:</p>
<p>&nbsp;</p>
<pre class="brush: php">function utf8_substr($str,$from,$len){
        # utf8 substr
        # www.yeap.lv
          return preg_replace('#^(?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$from.'}'.
                           '((?:[\x00-\x7F]|[\xC0-\xFF][\x80-\xBF]+){0,'.$len.'}).*#s',
                           '$1',$str);
    }
</pre>
<p>&nbsp;</p>
<p>After I have that function, the other one was simple:</p>
<p>&nbsp;</p>
<pre class="brush: php">function js_obfuscate($text,$step=4) {
   echo '<script type="text/javascript"><!--mce:0--></script>'.chr(10);
}
</pre>
<p>&nbsp;</p>
<p>$step defines how many characters the function will print in each  document.write call.</p>
<p>Hope you like it! If you find something better, you are free to leave  a comment! A encodeURI/decodeURI that works with UTF-8 would be great.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Fri, 09 Mar 2007 17:31:48 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=11&amp;set_lang=es">
<title><![CDATA[OpenRico Accordion implemented in a PHP Class]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=11&amp;set_lang=es</link>
<description><![CDATA[<p><a title="OpenRico homepage" href="http://www.openrico.org/">OpenRico</a> is an excellent tool for building dynamic AJAX-style sites but it also has beautiful javascript effects for your pages.</p>
<p>This is a simple PHP class that will help you build an Accordion effect. Code and example provided, just read a bit more!&nbsp;</p>
<p>file: ricoAccordion.class.php</p>
<pre class="brush: php"> 
/**
 * ricoAccordion Class v1.0
 * PHP wrapper class for create an openRico accordion
 * 
 * Created by jose.canciani (at) 4tm.biz
 * 
 * For updates check here:
 * http://www.4tm.biz/4tmsite/html/pensamos_post.php?id=11
 * 
 */
 
class ricoAccordion {
 
    var $name;
    var $style;
    var $class;
    var $accordionProps;
    
    var $panels;
    
    # switches, change as you like
    var $startPanel;
    
    function __construct() {
        $this-&gt;class = '';
        $this-&gt;style = '';
        $this-&gt;name = '';
        $this-&gt;accordionProps = array();
        $this-&gt;panels = array();
        $this-&gt;startPanel = 0;
    }
    
    function set($name, $value) {
        $this-&gt;{$name} = $value;
    }
    
    function load($header='',$content='') {
        $this-&gt;panels[] = array(
                            'header'=&gt;$header,
                            'content'=&gt;$content
        );        
    }
    
    
    function draw() {
    
        echo chr(10).'<div id="'.$this-&gt;name.'">class) {
            echo ' class="'.$this-&gt;class.'"';
        }
        if ($this-&gt;style) {
            echo ' style="'.$this-&gt;style.'"';
        }
        echo '&gt;'.chr(10);
        $i = 0;
        foreach ($this-&gt;panels as $panel) {
            $i++;
            echo ' <div id="panel'.$i.'">'.chr(10);
                  echo '  <div id="panel'.$i.'Header" class="accordionTabTitleBar">'.chr(10);
                  echo $panel['header'].chr(10);
                  echo '  </div>'.chr(10);
                  echo '  <div id="panel'.$i.'Content" class="accordionTabContentBox">'.chr(10);
                  echo $panel['content'];
                  echo '  </div>'.chr(10);
            echo ' </div>';
        }
        echo '</div>';
        
        echo '<script type="text/javascript"><!--mce:0--></script>'.chr(10);
    }
}

</pre>
<p>Before using this code remember that your HTML page should include  the prototype and rico javascript files (check openrico page for  examples).</p>
<p>So, here is how to use it:</p>
<pre class="brush: php"> 
## Construct the accordion
require_once('html/ricoAccordion.class.php');
 
// create class
$accordion = new ricoAccordion;
 
// add accordion name
$accordion-&gt;set('name','profileAccordion');
 
// set some style for the accordion
$accordion-&gt;set('style','width: 500px; margin-top: 20px; margin-left: 20px;');
 
// set some of the accordion properties
// (check the accordion source js class for all of them
$accordion-&gt;set('accordionProps',array(
                                        'panelHeight'=&gt;227,
                                        'collapsedBg'=&gt;'#80033E',
                                        'expandedBg'=&gt;'#80033E',
                                        'hoverBg'=&gt;'#80033E',
                                        'borderColor'=&gt;'#80033E'
                                    ));
 
// ...                                    
// Add the panel tabs here, replace $tabHeaderN for your
// title and $tabContentN for your content (N is the panel number)                                    
$accordion-&gt;load($tabHeader1,$tabContent1);
$accordion-&gt;load($tabHeader2,$tabContent2);
$accordion-&gt;load($tabHeader3,$tabContent3);
// ...
 
// you can set a starting panel to open at startup
// (panels begins at 0)
$accordion-&gt;set('startPanel',0);
 
// finally draw the accordion
$accordion-&gt;draw();

</pre>
<p>That&rsquo;s it! Accordion should be draw after the last line. Your tab  content can be almost anything. For example I&rsquo;ve implemented some ajax  forms for validate user data.</p>
<p>If you have any doubt or suggestion go ahead ad write!</p>
<p>Jose.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Mon, 05 Mar 2007 22:51:40 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=10&amp;set_lang=es">
<title><![CDATA[Bluetooth net between Nokia770 and Windows]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=10&amp;set_lang=es</link>
<description><![CDATA[<p>Since there is no wireless connection at the office and because Bluetooth is less power consuming than wifi, my friend (who has the Nokia 770) and I start searching the web for a Bluetooth network connection. This tutorial(-wannabe) is based in these two articles:</p>
<p><a href="http://maemo.org/maemowiki/HOWTO-BluetoothNetworking">http://maemo.org/maemowiki/HOWTO-BluetoothNetworking </a></p>
<p><a href="http://www.internettablettalk.com/wiki/index.php/Internet:Sharing_the_Internet_with_the_Nokia_770_over_Bluetooth">http://www.internettablettalk.com/.....okia_770_over_Bluetooth </a></p>
<p>First thing to do is pair the device with the PC, if you haven't already. You can do it by creating a new bluetooth connection in the Nokia 770. You will be asked to send a pair key which you should then type in your windows PC. If you have the choice click the checkbox to automatically connect to it next time. There's no need to create the connection once it's paired, just cancel it after the pairing has been successful.</p>
<p>If you have Nokia's latest OS (2006) you will have to run (as root) the following to load the bnep module (needed to set the interface later):</p>
<p>&nbsp;</p>
<pre class="brush: bash">insmod /mnt/initfs/lib/modules/current/bnep.ko
</pre>
<p>&nbsp;</p>
<p>We are assuming you have the Windows bluetooth driver correctly installed, and you have setup a bluetooth network interface with the following settings:</p>
<p>&nbsp;</p>
<pre class="brush: bash">Ethernet adapter Bluetooth Network:

        Connection-specific DNS Suffix  . :
        IP Address. . . . . . . . . . . . : 192.168.0.1
        Subnet Mask . . . . . . . . . . . : 255.255.255.0
        Default Gateway . . . . . . . . . :
</pre>
<p>&nbsp;</p>
<p>You can also set a gateway if you want to route all traffic to another interface (with bridge networking, which we will not cover it here).  Ok, now let's try to connect to the network. In the Nokia 770 run:</p>
<p>&nbsp;</p>
<pre class="brush: bash">hcitool scan 
pand --connect 00:10:20:30:40:50
</pre>
<p>&nbsp;</p>
<p>The hcitool scan will give you a bluetooth ID and name of all devices in range. Search for your computer and replace 00:10:20:30:40:50 for you computer's id.</p>
<p>Once you run the connect command your windows bluetooth driver should show a popup warning you about an incoming connection. Click on it and be sure to check the "Always allow" before clicking the allow bottom.</p>
<p>Now you are connected! If you run ifconfig you whould see the bnep0 interface. We have to configure it. Try this:</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre class="brush: bash">ifconfig bnep0 192.168.0.2 netmask 255.255.255.0 broadcast 192.168.0.255
</pre>
<p>&nbsp;</p>
<p>Running ifconfig again you should see it configured now. If you will use bridged connections on windows then you have to add a default gateway with the route command and setup your resolve.conf file. For this tutorial I recommend installing a proxy server like CCProxy (<a href="http://www.youngzsoft.net/">http://www.youngzsoft.net</a>).</p>
<p>You should now be able to ping each addresses and receive a response (be sure to disable firewalls if you have some).</p>
<p>Last thing to do is to install a dummy connection in the Nokia 770 because when you try to open a browser window it will ask you to connect first. Here is how:</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre class="brush: bash">gconftool -s -t string /system/osso/connectivity/IAP/DEFAULT/type DUMMY
</pre>
<p>&nbsp;</p>
<p>That's almost it, you just have to edit the DEFAULT connection you just created and setup the proxy servers. Use the IP of your windows bluetooth interface (192.68.0.1 for this example) and the ports where your proxy server is listening (808 for CCProxy by default).</p>
<p>I hope this quick steps can help you! For more information click on the links I've mention at the beginning of this post.</p>
<p>Jose.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Wed, 22 Nov 2006 14:27:32 GMT</dc:date>
</item>
<item rdf:about="http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=7&amp;set_lang=es">
<title><![CDATA[Anonymous browsing from the office]]></title>
<link>http://www.4tm.com.ar/4tmsite/es/pensamos.php?id=7&amp;set_lang=es</link>
<description><![CDATA[<p>I have some webpages I usually access from my office computer that I don't want to show in the company's access logs (don't think bad, usually it's just pages that are blocked becouse of the url words which are banned from the company's proxy).</p>
<p>This is a quick howto to bypass the proxy.</p>
<p>This article assumes you are able to do outgoing connections to a home server using an ssh port. If you don't, you can still manage to do it through your company's proxy if you setup your ssh daemon to listen to the 443 port. Usually the 443 port of the corporate's proxys are allowed to make permanent connections. Give it a try, it worked for me in my previous job (Putty program let's you use proxy servers).</p>
<p>I'm using Gentoo at home, so I just installed tinyproxy (emerge tinyproxy) and then edit /etc/tinyproxy/tinyproxy.conf and set the daemon to listen only to the internal interface. That will save us some time configuring access rules in the proxy.</p>
<p>&nbsp;</p>
<pre class="brush: bash"> 
Port 8888
Listen 127.0.0.1
Bind 127.0.0.1
</pre>
<p>&nbsp;</p>
<p>Now start the server (/etc/init.d/tinyproxy start).</p>
<p>So now we have Tinyproxy listening to port 8888 on the loopback interface. All we have to do know is connect from your office pc with ssh and do a port fowarding of your local 8888 port to your home's box 8888 port on the local interface. It should look something like this (with openssh):</p>
<p>&nbsp;</p>
<pre class="brush: bash"> 
ssh -L 8888:localhost:8888 user@homebox
</pre>
<p>&nbsp;</p>
<p>With Putty you set port fowarding in Connection -&gt; SSH -&gt; Tunnels: Source port: 8888, Destination: localhost:8888 and press Add. Once connected you should have you office pc's 8888 port fowarding to Tinyproxy. Now you just have to configure your browser to use a proxy server at localhost:8888. Firefox has several extensions for changing the proxy server from a tool bar bottom (for example xyzproxy).</p>
<p>That's it, you are know browsing anonymously (well, at least in your company, since you will be showing your home IP address to the sites you visit).</p>
<p>You can repeat this procedure using an anonymous proxy on the internet if you want full anonymity (is that a word??).</p>
<p>Happy browsing!</p>
<p>Jose.</p>]]></description>
<dc:creator>Jose Luis Canciani (josecanciani at Twitter)</dc:creator><dc:date>Mon, 20 Nov 2006 15:30:06 GMT</dc:date>
</item>
</rdf:RDF>
