<?xml version="1.0" encoding="windows-1252"?>
<node id="814095" title="Re: Persistent Fault Tolerant SQL (MySQL + DBI) connections!" created="2009-12-23 09:02:20" updated="2009-12-23 09:02:20">
<type id="11">
note</type>
<author id="747201">
afoken</author>
<data>
<field name="doctext">
&lt;p&gt;I literally had to look twice at the date of this posting. I had the strong feeling that I had stumbled over one of the ancient nodes created in the early stages of perlmonks.org, perhaps as a repost of much older code from the ages of Perl 4. But that posting is dated Dec 23, 2009.&lt;/p&gt;
&lt;p&gt;So, what's wrong with that code?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Perl 4 is dead. Don't beat that horse, it won't run any more. Prefixing a subroutine call with an ampersand does something very different in Perl 5 (disabling subroutine prototype checks) than it did in Perl 4 (just calling the function). And most times, you don't want that to happen. Drop that ampersand.&lt;/li&gt;
&lt;li&gt;Of course, your code explicitly states that &lt;c&gt;row_sql&lt;/c&gt;, &lt;c&gt;get_sql&lt;/c&gt;, and &lt;c&gt;hash_sql&lt;/c&gt; &lt;b&gt;DO NOT&lt;/b&gt; take any parameters - they have an empty prototype. Still, your code reads those parameters that should not exist. This works only when you explicitly disable prototype checks by prefixing the function name with an ampersand. Drop that empty prototypes.&lt;/li&gt;
&lt;li&gt;Perl 5 has modules. Don't put code into scripts loaded at runtime by [doc://do], use [doc://perlmod|modules]. This way, the namespace of the caller is not polluted. If you need to pollute the caller's namespace, use the [doc://Exporter] or newer modules like [mod://Sub::Exporter]. Preferably, export (pollute) only on demand.&lt;/li&gt;
&lt;li&gt;No [doc://strict], no [doc://warnings]. Both would have complained about several errors in the code.&lt;/li&gt;
&lt;li&gt;No way to setup the database connection, especially database driver, username and password, without changing the code for each and every application that wants to include this feature. I.e. you have to make a copy of this code for each application. This ends in a maintainance nightmare.&lt;/li&gt;
&lt;li&gt;High risk for SQL injection, because placeholders are not supported.&lt;/li&gt;
&lt;li&gt;Despite the effords to enable caching, the cache is poisoned by not using placeholders. So, performance will be far less than optimal.&lt;/li&gt;
&lt;li&gt;The code is limited to only one DB connection, without any obvious reason. Had this been written as a class with attributes instead of tons of global variables, this limit would not exist.&lt;/li&gt;
&lt;li&gt;&lt;c&gt;print&lt;/c&gt; and &lt;c&gt;exit&lt;/c&gt; instead of &lt;c&gt;die&lt;/c&gt; prevents any attempts of error recovery. And error messages end where they don't belong: &lt;c&gt;STDOUT&lt;/c&gt;.&lt;/li&gt;
&lt;li&gt;&lt;c&gt;eval&lt;/c&gt; blocks that hide errors, e.g. while fetching data rows from the database. Again, this prevents error recovery, simply because nobody can detect errors! Either don't use &lt;c&gt;eval&lt;/c&gt; or re-throw the errors.&lt;/li&gt;
&lt;li&gt;No concept of how to indent code. Instead, a wild mix of tabs, spaces, and missing newlines. This makes the code hard to read, hard to understand, hard to maintain.&lt;/li&gt;
&lt;li&gt;A constantly increased index counter (&lt;c&gt;$c&lt;/c&gt;) in &lt;c&gt;get_sql()&lt;/c&gt;. Why don't you just use &lt;c&gt;push @results,$row[0]&lt;/c&gt;?
&lt;li&gt;Reinventing the wheel in inefficient perl code. Had you read the [mod://DBI] manual, you would have found that your &lt;c&gt;get_sql&lt;/c&gt; is a poor reimplementation of DBI's &lt;c&gt;selectcol_arrayref&lt;/c&gt;. Of course, your &lt;c&gt;hash_sql&lt;/c&gt; function has a superiour DBI equivalent: &lt;c&gt;selectall_hashref&lt;/c&gt;. And your &lt;c&gt;row_sql&lt;/c&gt; is the dumb cousin of DBI's &lt;c&gt;selectrow_array&lt;/c&gt;. All of those DBI methods are written in fast C/XS code (with pure-perl fallback routines).&lt;/li&gt;
&lt;li&gt;Your code is not safe for transactions. Reconnecting to the database ends an old transaction (does it rollback or commit?) and creates a new one, damaging data.&lt;/li&gt;
&lt;li&gt;What is the reason for &lt;c&gt;del_sql&lt;/c&gt;, &lt;c&gt;mod_sql&lt;/c&gt;, and &lt;c&gt;put_sql&lt;/c&gt;? They are all just wrappers for &lt;c&gt;sql&lt;/c&gt;. And instead of aborting with a useful error message when called with an empty SQL statement, they print a warning and CONTINUE calling &lt;c&gt;sql&lt;/c&gt;. &lt;c&gt;sql&lt;/c&gt; still aborts, but gives a wrong error message.&lt;/li&gt;
&lt;li&gt;What's the purpose of the various &lt;c&gt;sleep(1)&lt;/c&gt; calls?&lt;/li&gt;
&lt;li&gt;What's the purpose of &lt;c&gt;$retry_count&lt;/c&gt;? It is incremented quite often, but never compared to anything.&lt;/li&gt;
&lt;li&gt;Why do you emit "warnings" to STDOUT using print instead of using [doc://warn]? &lt;c&gt;warn&lt;/c&gt; allows your caller to handle warnings (&lt;c&gt;$SIG{__WARN__}&lt;/c&gt;), and unless your caller traps them, they end where they belong: STDERR.&lt;/li&gt;
&lt;li&gt;[mod://DBIx::AutoReconnect] implements the only "new" feature in your code, i.e. automatic reconnect to the database. But &lt;c&gt;DBIx::AutoReconnect&lt;/c&gt; implements this feature in a way that is nearly completely transparent to the application. You just add &lt;c&gt;use DBIx::AutoReconnect&lt;/c&gt; to your application, and change &lt;c&gt;DBI-&gt;connect(...)&lt;/c&gt; to &lt;c&gt;DBIx::AutoReconnect-&gt;connect(...)&lt;/c&gt;, and every other feature of DBI behaves as usual. Of course, it works with every database, and with all &lt;c&gt;connect&lt;/c&gt; parameters. It adds three additional connect attributes that allow fine-tuning. So, what is the advantage of your code over the nearly five year old &lt;c&gt;DBIx::AutoReconnect&lt;/c&gt; that's available for free at CPAN?&lt;/li&gt;
&lt;li&gt;And finally: &lt;i&gt;Use sql.pl as you wish, its&lt;/i&gt; &amp;#91;sic&amp;#93; &lt;i&gt;now yours.&lt;/i&gt; This reads like: Take this crap and don't ever ask me for help. (OK, one could also read this as "I hereby put this code into public domain.")&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I would recommend to use this sql.pl only as a example of how &lt;b&gt;not&lt;/b&gt; to write Perl code. In clear words: &lt;b&gt;DO NOT USE THIS CRAP!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Code like this is responsible for Perls bad reputation. So: &lt;tt&gt;&lt;b&gt;--&lt;/b&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Alexander&lt;/p&gt;
&lt;div class="pmsig"&gt;&lt;div class="pmsig-747201"&gt;
--&lt;br&gt;
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
&lt;/div&gt;&lt;/div&gt;</field>
<field name="root_node">
814072</field>
<field name="parent_node">
814072</field>
</data>
</node>
