<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Chip's Tips for Developers » SQL</title>
	
	<link>http://www.chipstips.com</link>
	<description>Contains coding, but not narcotic.</description>
	<lastBuildDate>Mon, 04 Feb 2013 20:50:40 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/ChipsTipsSql" /><feedburner:info uri="chipstipssql" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Averting an unintentional DOS attack on my spam filter</title>
		<link>http://feedproxy.google.com/~r/ChipsTipsSql/~3/Xu4Vm7Or2SU/</link>
		<comments>http://www.chipstips.com/?p=565#comments</comments>
		<pubDate>Sat, 13 Nov 2010 22:33:17 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[rbisspam]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=565</guid>
		<description><![CDATA[The other day, my incoming stream of email froze. After narrowing down the culprit, it turned out to be my spam filter choking on one particular message. My son John had sent me an 11MB mpg attachment (Queensryche&#8217;s &#8220;Art of Life&#8221;), and I hadn&#8217;t anticipated that breaking a message of that size into words and [...]]]></description>
				<content:encoded><![CDATA[<div class="socialize-in-content socialize-in-content-right"><div class="socialize-in-button socialize-in-button-right"><g:plusone size="tall" href="http://www.chipstips.com/?p=565"></g:plusone></div><div class="socialize-in-button socialize-in-button-right"><a href="http://twitter.com/share" class="twitter-share-button" data-url="http://www.chipstips.com/?p=565" data-text="Averting an unintentional DOS attack on my spam filter" data-count="vertical" data-via="socializeWP" ><!--Tweetter--></a></div><div class="socialize-in-button socialize-in-button-right"><iframe src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.chipstips.com%2F%3Fp%3D565&amp;send=&amp;layout=box_count&amp;width=50&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font=arial&amp;height=65" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:50px; height:65px;" allowTransparency="true"></iframe></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">
			<!-- 
			reddit_url = "http://www.chipstips.com/?p=565";
			reddit_title = "Averting an unintentional DOS attack on my spam filter";	//-->
		</script><script type="text/javascript" src="http://www.reddit.com/static/button/button2.js"></script></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">var dzone_url = "http://www.chipstips.com/?p=565";</script>
		<script type="text/javascript">var dzone_title = "Averting an unintentional DOS attack on my spam filter";</script>
		<script type="text/javascript">
			<!-- 
			var dzone_style = "1";
			//-->
		</script><script language="javascript" src="http://widgets.dzone.com/links/widgets/zoneit.js"></script></div></div><p>The other day, my incoming stream of email froze.  After narrowing down the culprit, it turned out to be my spam filter choking on one particular message.  My son John had sent me an 11MB mpg attachment (Queensryche&#8217;s &#8220;Art of Life&#8221;), and I hadn&#8217;t anticipated that breaking a message of that size into words and checking each combination of one, two, and three adjacent words would require such a long time that the POP connection getmail relied upon timed out.</p>
<p>So, I added a switch to isspam (-M, or &#8211;Maxsize) that allows you to exclude any message over a specified size.  Links below.</p>
<div class="downloadbuttons">
    <a href="http://bitbucket.org/sterlingcamden/isspam"><img src="http://www.chipstips.com/images/bitbucket.png" alt="BitBucket repository" /></a><br />
    <a href="http://www.chipstips.com/download/rbisspam.tar.bz2"><img src="http://www.chipstips.com/images/tarball.png" alt="tarball" /></a>
</div>
<img src="http://feeds.feedburner.com/~r/ChipsTipsSql/~4/Xu4Vm7Or2SU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=565</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=565</feedburner:origLink></item>
		<item>
		<title>More fun with SQLite3/Ruby</title>
		<link>http://feedproxy.google.com/~r/ChipsTipsSql/~3/ZuVQFPeYoU4/</link>
		<comments>http://www.chipstips.com/?p=548#comments</comments>
		<pubDate>Wed, 25 Aug 2010 05:02:47 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[rbisspam]]></category>
		<category><![CDATA[sqlite3]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=548</guid>
		<description><![CDATA[See Chip sweat Not knowing where to look for SQLite3 maximums at the time (here they are), I became a little nervous as my Bayesian spam filter database approached 2 gigabytes. When it passed that milestone and continued working, I breathed a sigh of relief. Then one day, I noticed that the spam totals were [...]]]></description>
				<content:encoded><![CDATA[<div class="socialize-in-content socialize-in-content-right"><div class="socialize-in-button socialize-in-button-right"><g:plusone size="tall" href="http://www.chipstips.com/?p=548"></g:plusone></div><div class="socialize-in-button socialize-in-button-right"><a href="http://twitter.com/share" class="twitter-share-button" data-url="http://www.chipstips.com/?p=548" data-text="More fun with SQLite3/Ruby" data-count="vertical" data-via="socializeWP" ><!--Tweetter--></a></div><div class="socialize-in-button socialize-in-button-right"><iframe src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.chipstips.com%2F%3Fp%3D548&amp;send=&amp;layout=box_count&amp;width=50&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font=arial&amp;height=65" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:50px; height:65px;" allowTransparency="true"></iframe></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">
			<!-- 
			reddit_url = "http://www.chipstips.com/?p=548";
			reddit_title = "More fun with SQLite3/Ruby";	//-->
		</script><script type="text/javascript" src="http://www.reddit.com/static/button/button2.js"></script></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">var dzone_url = "http://www.chipstips.com/?p=548";</script>
		<script type="text/javascript">var dzone_title = "More fun with SQLite3/Ruby";</script>
		<script type="text/javascript">
			<!-- 
			var dzone_style = "1";
			//-->
		</script><script language="javascript" src="http://widgets.dzone.com/links/widgets/zoneit.js"></script></div></div><h3>See Chip sweat</h3>
<p>Not knowing where to look for SQLite3 maximums at the time (<a href="http://sqlite.org/limits.html">here they are</a>), I became a little nervous as my <a href="http://www.chipstips.com/?p=533">Bayesian spam filter</a> database approached 2 gigabytes.  When it passed that milestone and continued working, I breathed a sigh of relief.  Then one day, I noticed that the spam totals were wrong &#8212; my total number of spam messages had been replaced with a nil!</p>
<p>Fortunately, I discovered the problem on the day it first occurred.  I restored the database from the previous day&#8217;s backup &#8212; then started to try to figure out what was wrong.  I was just about certain that 2GB was the culprit.  Probably a pointer being cast to int somewhere in the implementation of SQLite3 or the gem.  Using irb I verified that I could reproduce the problem with a simple insert.  So I logged an issue on the <a href="https://groups.google.com/group/sqlite3-ruby">sqlite3-ruby Google Group</a>.</p>
<p>Luis Lavena responded almost immediately.  In trying to help him reproduce the problem without uploading 2GB of data, I tried out each step myself as well.  Suddenly I found that I couldn&#8217;t get an insert to work, even on an empty database!  That let the 2GB boundary off the hook.  What could I have possibly screwed up?</p>
<p>It turns out that all I had done was to upgrade the gem, which came through on a portupgrade.  Even though I still can&#8217;t find it anywhere in the docs, Luis let me know that the parameters to a query need to be passed as an array now, instead of as individual arguments (UPDATE: <a href="http://github.com/luislavena/sqlite3-ruby/commit/43dd1e0024aa33efbc293303859a61e6ccb69a23">it&#8217;s been added now</a>).  Be warned, and please upgrade your copy of isspam before you get version 1.3.0 or greater of the sqlite3 gem.  As usual, the tarball link is below, or you can pull it from <a href="http://bitbucket.org/sterlingcamden/isspam">Bitbucket</a>.</p>
<p>In case you&#8217;re curious, SQLite3 supports a maximum of 1073741823 pages out of the box, with a default page size of 1024.  That means a maximum file size of about 1 terabyte.  But if you tweak the page size, you can approach 32 terabytes.</p>
<h3>See Chip smack his forehead</h3>
<p>I had a V-8 moment when I discovered that the <a href="http://github.com/luislavena/sqlite3-ruby#readme">sqlite3 rubygem</a> allows you to pass a code block to its database queries.  That block gets called once per row as they are being extracted from the database.  That made my <a href="http://www.chipstips.com/?p=546">paged query</a> obsolete, so I ripped it out.</p>
<h3>See Chip code another feature just for the fun of it</h3>
<p>Ever wonder how an individual word affects spam score?  Of course, you could always do <code>echo <em>word</em> | isspam -p</code>, but that only gives you that word&#8217;s spam score.  It doesn&#8217;t tell you how many times that word has been seen in spam or non-spam.  So I added a -w option, which takes all the phrases from the input and gives you the detailed statistics on those phrases.  You can control the length of phrase to analyze with -m, so if you only want individual words, use -m 1.</p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbisspam.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>
<img src="http://feeds.feedburner.com/~r/ChipsTipsSql/~4/ZuVQFPeYoU4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=548</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=548</feedburner:origLink></item>
		<item>
		<title>Handling mammoth queries in SQLite/Ruby</title>
		<link>http://feedproxy.google.com/~r/ChipsTipsSql/~3/smuNogqFTqc/</link>
		<comments>http://www.chipstips.com/?p=546#comments</comments>
		<pubDate>Thu, 05 Aug 2010 01:23:24 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[rbisspam]]></category>
		<category><![CDATA[sqlite3]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=546</guid>
		<description><![CDATA[As my isspam Bayesian spam filter learns about the spamminess of new words and phrases, its SQLite database grows correspondingly. It&#8217;s now up to 1.8 gigabytes, containing more than 6.5 million unique phrases. Because it&#8217;s an SQL database, the size doesn&#8217;t impact performance much, except when gathering database-wide statistics. The &#8216;isspam -d&#8217; command, which dumps [...]]]></description>
				<content:encoded><![CDATA[<div class="socialize-in-content socialize-in-content-right"><div class="socialize-in-button socialize-in-button-right"><g:plusone size="tall" href="http://www.chipstips.com/?p=546"></g:plusone></div><div class="socialize-in-button socialize-in-button-right"><a href="http://twitter.com/share" class="twitter-share-button" data-url="http://www.chipstips.com/?p=546" data-text="Handling mammoth queries in SQLite/Ruby" data-count="vertical" data-via="socializeWP" ><!--Tweetter--></a></div><div class="socialize-in-button socialize-in-button-right"><iframe src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.chipstips.com%2F%3Fp%3D546&amp;send=&amp;layout=box_count&amp;width=50&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font=arial&amp;height=65" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:50px; height:65px;" allowTransparency="true"></iframe></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">
			<!-- 
			reddit_url = "http://www.chipstips.com/?p=546";
			reddit_title = "Handling mammoth queries in SQLite/Ruby";	//-->
		</script><script type="text/javascript" src="http://www.reddit.com/static/button/button2.js"></script></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">var dzone_url = "http://www.chipstips.com/?p=546";</script>
		<script type="text/javascript">var dzone_title = "Handling mammoth queries in SQLite/Ruby";</script>
		<script type="text/javascript">
			<!-- 
			var dzone_style = "1";
			//-->
		</script><script language="javascript" src="http://widgets.dzone.com/links/widgets/zoneit.js"></script></div></div><p>As my <a href="http://www.chipstips.com/?p=533">isspam Bayesian spam filter</a> learns about the spamminess of new words and phrases, its SQLite database grows correspondingly.  It&#8217;s now up to 1.8 gigabytes, containing more than 6.5 million unique phrases.  Because it&#8217;s an SQL database, the size doesn&#8217;t impact performance much, except when gathering database-wide statistics.  The &#8216;isspam -d&#8217; command, which dumps all of the data to stdout, would run for hours and exhaust available memory (in the previous version).  That&#8217;s because <a href="http://sqlite-ruby.rubyforge.org/">SQLite/Ruby</a> wants to return the result of a SELECT operation as an array of rows.  6.5 <strong>million</strong> rows.</p>
<p>That just won&#8217;t do, especially as the database continues to grow.  So I reworked the &#8216;dump&#8217; option to read the database in pages of 10,000 rows at a time on one thread, while the main thread formats the output to the screen.  I generalized the concept slightly by monkeypatching SQLite3::Database to add a &#8216;paged_execute&#8217; method to which you can pass the desired page size, and which returns a PagedRows object that has an  &#8216;each&#8217; method and a &#8216;count&#8217; attribute.  Thus, you can use it in place of a Database#execute for most operations that will potentially return an enormous number of rows.</p>
<p>The main thread self-balances its sleep time between checking for input from the database query thread, in order to minimize overall runtime.</p>
<p>I&#8217;ve included the code and docs in the tarball below, or you can grab them from the <a href="http://bitbucket.org/sterlingcamden/isspam/">BitBucket repository</a>.  But in case you&#8217;re not interested in all that spam filter code, here&#8217;s the bit that adds paged queries to SQLite3::Database:</p>
<pre><code>
# class to create an array-like set of paged database query results
class PagedRows
  # count of items retrieved so far
  attr :count

  # Create a paged query
  #  db = SQLite3::Database
  #  pagesize = int, number of rows per query
  #  query = select statement without limit or offset
  #  param = parameter for query
  def initialize(db, pagesize, query, param)
    @db = db
    @query = query
    @param = param
    @pagesize = pagesize
    @count = 0
  end

  # Iterate over all rows from multiple queries
  def each(&#038;block)
    queue = []			# pages yet to be processed
    done = false
    mutex = Mutex.new

    # worker thread for database queries
    t = Thread.new {
      offset = 0
      begin
	# grab a page
        enq = @db.execute(@query + " LIMIT #{@pagesize} OFFSET #{offset}", @param)
	mutex.synchronize do
	  queue << enq		# add to queue
	end
        offset += @pagesize
        @count += enq.size
      end until enq.size < @pagesize
      enq = nil			# so last page gets released after processing
      done = true
    }

    nsleep = 0.5		# how long to wait for worker thread
    deq = nil			# establish scope of deq
    while (!done)
      sleep nsleep
      while queue.size > 0	# any rows already fetched?
	mutex.synchronize do
       	  deq = queue.shift
        end
	deq.each &#038;block		# process the page of rows
	deq = nil		# release for GC
	Thread.pass		# give the worker thread a chance
	nsleep = [0.001, (nsleep*0.6666666667)].max	# Ok, we waited long enough (will be bumped below)
      end
      nsleep *= 1.5		# wait a little longer next time
    end

    t.join			# this should be a formality
  end
end

class SQLite3::Database
  # Create a paged query
  #  pagesize = number of rows per query
  #  query = database select statement
  #  param = parameter for query
  def paged_execute(pagesize, query, param)
    PagedRows.new self, pagesize, query, param
  end
end
</pre>
<p></code></p>
<div class="downloadbutton"><a href="http://www.chipstips.com/download/rbisspam.tar.bz2"><img src="http://www.chipstips.com/images/download.gif" alt="download" /></a></div>
<img src="http://feeds.feedburner.com/~r/ChipsTipsSql/~4/smuNogqFTqc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=546</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=546</feedburner:origLink></item>
		<item>
		<title>WordPress tag cloud widget version 2.0 for Jerome’s Keywords 2.0</title>
		<link>http://feedproxy.google.com/~r/ChipsTipsSql/~3/3IlouOEZW8s/</link>
		<comments>http://www.chipstips.com/?p=149#comments</comments>
		<pubDate>Wed, 06 Sep 2006 19:04:31 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Wildly popular]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[jeromeskeywords]]></category>
		<category><![CDATA[phptagcloud]]></category>
		<category><![CDATA[phptagcloud2]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[sidebar]]></category>
		<category><![CDATA[tagcloud]]></category>
		<category><![CDATA[tagging]]></category>
		<category><![CDATA[widget]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=149</guid>
		<description><![CDATA[Jerome has released a 2.0 beta version of his famous Jerome&#8217;s Keywords plugin, on which my tag cloud widget is based. Some of the functions I used in my widget are no longer available, so the previous version of my widget will not work with it. Thus, I have updated the widget, which you can [...]]]></description>
				<content:encoded><![CDATA[<div class="socialize-in-content socialize-in-content-right"><div class="socialize-in-button socialize-in-button-right"><g:plusone size="tall" href="http://www.chipstips.com/?p=149"></g:plusone></div><div class="socialize-in-button socialize-in-button-right"><a href="http://twitter.com/share" class="twitter-share-button" data-url="http://www.chipstips.com/?p=149" data-text="WordPress tag cloud widget version 2.0 for Jerome&#8217;s Keywords 2.0" data-count="vertical" data-via="socializeWP" ><!--Tweetter--></a></div><div class="socialize-in-button socialize-in-button-right"><iframe src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.chipstips.com%2F%3Fp%3D149&amp;send=&amp;layout=box_count&amp;width=50&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font=arial&amp;height=65" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:50px; height:65px;" allowTransparency="true"></iframe></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">
			<!-- 
			reddit_url = "http://www.chipstips.com/?p=149";
			reddit_title = "WordPress tag cloud widget version 2.0 for Jerome&#8217;s Keywords 2.0";	//-->
		</script><script type="text/javascript" src="http://www.reddit.com/static/button/button2.js"></script></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">var dzone_url = "http://www.chipstips.com/?p=149";</script>
		<script type="text/javascript">var dzone_title = "WordPress tag cloud widget version 2.0 for Jerome&#8217;s Keywords 2.0";</script>
		<script type="text/javascript">
			<!-- 
			var dzone_style = "1";
			//-->
		</script><script language="javascript" src="http://widgets.dzone.com/links/widgets/zoneit.js"></script></div></div><p><img width="128" vspace="10" hspace="10" height="61" align="left" alt="vapourtrails" src="http://www.chipstips.com/wp-content/uploads/2006/09/vapourtrails.jpg" />Jerome has released a <a href="http://vapourtrails.ca/2006-08/keywords-20-beta">2.0 beta version of his famous Jerome&#8217;s Keywords plugin</a>, on which my tag cloud widget is based.  Some of the functions I used in my widget are no longer available, so the previous version of my widget will not work with it.  Thus, I have updated the widget, which you can download below.</p>
</p>
<p>Jerome seems to have fixed the problems in the plugin itself that I mentioned <a href="http://www.chipsquips.com/?p=408">here</a> and <a href="http://www.chipsquips.com/?p=468">here</a>.  When he releases this version from beta, I&#8217;ll remove my corrected versions.  I&#8217;ll go ahead now and add updates to let people know about the beta.</p>
</p>
<p><img width="320" vspace="10" hspace="10" height="217" align="left" alt="tagcloudoptions" src="http://www.chipstips.com/wp-content/uploads/2006/09/tagcloudoptions.jpg" />Jerome also included all of my widget&#8217;s options (other than Title) in a new &#8220;Tag Cloud Display&#8221; section of the plugin&#8217;s options.  So, I have removed those options from the widget option panel.  Please go into &#8220;Options/Jerome&#8217;s Keywords&#8221; and update your tag cloud options there.</p>
</p>
<p>Note also that this version of the widget is not compatible with earlier versions of the plugin.  If you need a widget for earlier versions of Jerome&#8217;s Keywords, get version 1.1 of the widget <a href="http://chipstips.com/?p=133">here</a>.</p>
<p>UPDATE: Get the latest version with improved cache control <a href="http://www.chipstips.com/?p=152">here.</a></p>
</p>
<p><a href="http://www.chipstips.com/download/phptagcloud2.zip"><img src="http://www.chipstips.com/images/download.gif" /></a></p>
<img src="http://feeds.feedburner.com/~r/ChipsTipsSql/~4/3IlouOEZW8s" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=149</wfw:commentRss>
		<slash:comments>67</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=149</feedburner:origLink></item>
		<item>
		<title>Magic quotes correction for Jerome’s Keywords WordPress plugin</title>
		<link>http://feedproxy.google.com/~r/ChipsTipsSql/~3/uHl0yKNrMBc/</link>
		<comments>http://www.chipstips.com/?p=148#comments</comments>
		<pubDate>Fri, 04 Aug 2006 17:42:58 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[Wildly popular]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[clusty]]></category>
		<category><![CDATA[escaping]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[jeromeskeywords]]></category>
		<category><![CDATA[johannesjarolim]]></category>
		<category><![CDATA[mikekoepke]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[phpjk21]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[quoting]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=148</guid>
		<description><![CDATA[Mike Koepke reported a problem he was having with the Jerome&#8217;s Keywords plugin when he searched on a tag that contained a single quote: WordPress database error: [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 's Creek' OR [...]]]></description>
				<content:encoded><![CDATA[<div class="socialize-in-content socialize-in-content-right"><div class="socialize-in-button socialize-in-button-right"><g:plusone size="tall" href="http://www.chipstips.com/?p=148"></g:plusone></div><div class="socialize-in-button socialize-in-button-right"><a href="http://twitter.com/share" class="twitter-share-button" data-url="http://www.chipstips.com/?p=148" data-text="Magic quotes correction for Jerome&#8217;s Keywords WordPress plugin" data-count="vertical" data-via="socializeWP" ><!--Tweetter--></a></div><div class="socialize-in-button socialize-in-button-right"><iframe src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.chipstips.com%2F%3Fp%3D148&amp;send=&amp;layout=box_count&amp;width=50&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font=arial&amp;height=65" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:50px; height:65px;" allowTransparency="true"></iframe></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">
			<!-- 
			reddit_url = "http://www.chipstips.com/?p=148";
			reddit_title = "Magic quotes correction for Jerome&#8217;s Keywords WordPress plugin";	//-->
		</script><script type="text/javascript" src="http://www.reddit.com/static/button/button2.js"></script></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">var dzone_url = "http://www.chipstips.com/?p=148";</script>
		<script type="text/javascript">var dzone_title = "Magic quotes correction for Jerome&#8217;s Keywords WordPress plugin";</script>
		<script type="text/javascript">
			<!-- 
			var dzone_style = "1";
			//-->
		</script><script language="javascript" src="http://widgets.dzone.com/links/widgets/zoneit.js"></script></div></div><p><a href="http://www.mikekoepke.com/">Mike Koepke</a> reported a <a href="#comment-341">problem</a> he was having with the <a href="http://www.chipstips.com/?p=146">Jerome&#8217;s Keywords</a> plugin when he searched on a tag that contained a single quote:</p>
<p><code>WordPress database error: [You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 's Creek' OR jkeywords_meta.meta_value LIKE 'Wilson's Creek,%' OR jkeywords_meta.' at line 1] SELECT DISTINCT * FROM wp_posts LEFT JOIN wp_postmeta AS jkeywords_meta ON (wp_posts.ID = jkeywords_meta.post_id) WHERE 1=1 AND post_date_gmt<br />
</code></p>
<p>I was unable to reproduce the behavior on my version, but it appears from the error message that the quote is not getting &#8220;<a rel="nofollow" href="http://en.wikipedia.org/wiki/String_literal#Including_Quotation_Marks">quoted</a>&#8220;. So, I added the following line to the &#8220;keywords_postsWhere&#8221; function in jeromes-keywords.php:</p>
<p><code>$keyword = str_replace("'", "''", $keyword);</code></p>
<p>Using the simple <a href="http://sunsite.mff.cuni.cz/MIRRORS/ftp.mysql.com/doc/en/String_syntax.html">quote-doubling</a> escape route. This solved the problem for Mike, but it didn&#8217;t work with my version, and from the resulting error message I could see that in my version the quote was already being escaped via a backslash (\). Modifying my fix to use backslash quoting didn&#8217;t work for me, either, because then the backslash gets backslashed.</p>
<p>Comparing versions, I was using MySQL 3.23.54 with PHP 4.4.1. Mike is on MySQL 4.1.14 and PHP 5.0.4. Then Mike tried this out on a local server with MySQL 5.0.22 and PHP 5.14, and got the same results as I did. So we both concluded that something got broken in MySQL or PHP between my early version and Mike&#8217;s live version, but has since been fixed again. Or was there some option that needed enabling on Mike&#8217;s live server? My Google searches for an explanation of this phenomenon were fruitless.</p>
<p><a href="http://clusty.com">Clusty</a> to the rescue. <a href="http://clusty.com/search?query=mysql+escaping+quotes&#038;sourceid=Mozilla-search">Clusty</a> lead me to <a href="http://www.erikburrows.com/index.php?node=PHP%2FPostgreSQL+String+Quoting">this post</a>. It turns out that PHP has a runtime option, <a href="http://us2.php.net/manual/en/ref.info.php#ini.magic-quotes-gpc">magic_quotes_gpc</a>, that is enabled by default. This option automagically escapes quotes, backslashes, and nulls in a GET, POST, or cookie with a backslash. Bingo.</p>
<p>As with most (all?) PHP options, you can query the setting of magic_quotes_gpc at runtime via (20 guesses?) <a href="http://us2.php.net/manual/en/function.get-magic-quotes-gpc.php">get_magic_quotes_gpc()</a>, natch. So I changed my fix as follows:</p>
<p><code>if (!get_magic_quotes_gpc()) {<br />
$keyword = addslashes($keyword);<br />
}</code></p>
<p>You can download the corrected version below. If <a href="http://vapourtrails.ca/wp-keywords">Jerome</a> would like to adopt this fix in his master version, I&#8217;ll take down my copy and point to him instead.  <a href="http://johannes.jarolim.com/blog/">Johannes</a>, you might want to implement a similar test in your <a href="http://johannes.jarolim.com/blog/2006/07/05/jeromes-keyword-manager-final-destination/">Jerome&#8217;s Keywords Manager</a> plugin.  Sorry to give you one more place to go after your &#8220;final destination&#8221;.</p>
<p>UPDATE: Jerome has released a 2.0 beta version of the plugin that appears to address this issue.  Mike Koepke, can you verify?  If you&#8217;re using my tag cloud widget, you&#8217;ll need <a href="http://www.chipstips.com/?p=149">version 2.0</a> of that as well.</p>
<div><a href="http://www.chipstips.com/download/phpjk21.zip"><img src="http://www.chipstips.com/images/download.gif" /></a></div>
<img src="http://feeds.feedburner.com/~r/ChipsTipsSql/~4/uHl0yKNrMBc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=148</wfw:commentRss>
		<slash:comments>7</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=148</feedburner:origLink></item>
		<item>
		<title>Correction for Jerome’s Keywords plugin for WordPress</title>
		<link>http://feedproxy.google.com/~r/ChipsTipsSql/~3/3uSEoUdqlAI/</link>
		<comments>http://www.chipstips.com/?p=146#comments</comments>
		<pubDate>Fri, 14 Jul 2006 16:31:34 +0000</pubDate>
		<dc:creator>Sterling Camden</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Wildly popular]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[jeromeskeywords]]></category>
		<category><![CDATA[johannesjarolim]]></category>
		<category><![CDATA[like]]></category>
		<category><![CDATA[phpjerome]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[sidebar]]></category>
		<category><![CDATA[widget]]></category>

		<guid isPermaLink="false">http://www.chipstips.com/?p=146</guid>
		<description><![CDATA[I use the Jerome&#8217;s Keywords plugin for WordPress to do tagging on my blogs. I love it, but it has one problem: if you search on a tag that is contained within another tag, you get the aggregate results of both tags. That&#8217;s because Jerome uses a SQL WHERE clause containing LIKE &#8216;%keyword%&#8217; to match [...]]]></description>
				<content:encoded><![CDATA[<div class="socialize-in-content socialize-in-content-right"><div class="socialize-in-button socialize-in-button-right"><g:plusone size="tall" href="http://www.chipstips.com/?p=146"></g:plusone></div><div class="socialize-in-button socialize-in-button-right"><a href="http://twitter.com/share" class="twitter-share-button" data-url="http://www.chipstips.com/?p=146" data-text="Correction for Jerome&#8217;s Keywords plugin for WordPress" data-count="vertical" data-via="socializeWP" ><!--Tweetter--></a></div><div class="socialize-in-button socialize-in-button-right"><iframe src="//www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.chipstips.com%2F%3Fp%3D146&amp;send=&amp;layout=box_count&amp;width=50&amp;show_faces=false&amp;action=like&amp;colorscheme=light&amp;font=arial&amp;height=65" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:50px; height:65px;" allowTransparency="true"></iframe></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">
			<!-- 
			reddit_url = "http://www.chipstips.com/?p=146";
			reddit_title = "Correction for Jerome&#8217;s Keywords plugin for WordPress";	//-->
		</script><script type="text/javascript" src="http://www.reddit.com/static/button/button2.js"></script></div><div class="socialize-in-button socialize-in-button-right"><script type="text/javascript">var dzone_url = "http://www.chipstips.com/?p=146";</script>
		<script type="text/javascript">var dzone_title = "Correction for Jerome&#8217;s Keywords plugin for WordPress";</script>
		<script type="text/javascript">
			<!-- 
			var dzone_style = "1";
			//-->
		</script><script language="javascript" src="http://widgets.dzone.com/links/widgets/zoneit.js"></script></div></div><p>I use the <a href="http://vapourtrails.ca/wp-keywords">Jerome&#8217;s Keywords plugin</a> for <a href="http://wordpress.org">WordPress</a> to do tagging on my blogs.  I love it, but it has <a href="http://www.chipsquips.com/?p=372">one problem</a>: if you search on a tag that is contained within another tag, you get the aggregate results of both tags.  That&#8217;s because Jerome uses a SQL WHERE clause containing LIKE &#8216;%keyword%&#8217; to match tags that are in a comma-delimited list in the metadata value field.</p>
<p>I left a comment on <a href="http://vapourtrails.ca/wp-keywords">Jerome&#8217;s blog</a> about this issue, but it looks like it never even made it past moderation.  In fact, no new comments on this post since May.  Where&#8217;s Jerome?</p>
<p>I <a href="http://www.chipsquips.com/?p=386">mentioned this problem</a> to <a href="http://johannes.jarolim.com">Johannes Jarolim</a>, author of the <a href="http://johannes.jarolim.com/blog/2006/07/05/jeromes-keyword-manager-final-destination/">Jerome&#8217;s Keywords Manager plugin</a>.  Johannes came up with the correct WHERE sub-clause (value LIKE &#8216;keyword,%&#8217; OR value LIKE &#8216;%,keyword&#8217; OR value LIKE &#8216;%,keyword,%&#8217; OR value = &#8216;keyword&#8217;) and implemented that in his plugin.  But alas, that did not help my <a href="http://chipstips.com/?tag=phptagcloud">widget</a>, which relies on the ?tag=keyword link operation provided by Jerome.</p>
<p>So, I felt like my best alternative would be to correct Jerome&#8217;s plugin and post it here.  If Jerome adopts this correction (or something equivalent) and lets me know about it, I&#8217;ll take down this download and point over to his site instead.</p>
<p>UPDATE:  I made yet another correction to this plugin, so head on <a href="http://chipstips.com/?tag=phpjk21">over here</a> for the latest and greatest.</p>
<p>UPDATE:  Jerome released a 2.0 beta of the plugin that corrects this problem.  <a href="http://www.chipstips.com/?p=149">Get the whole scoop</a>.</p>
<div><a href="http://www.chipstips.com/download/phpjerome.zip"><img src="http://www.chipstips.com/images/download.gif" /></a></div>
<img src="http://feeds.feedburner.com/~r/ChipsTipsSql/~4/3uSEoUdqlAI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.chipstips.com/?feed=rss2&amp;p=146</wfw:commentRss>
		<slash:comments>12</slash:comments>
		<feedburner:origLink>http://www.chipstips.com/?p=146</feedburner:origLink></item>
	</channel>
</rss>
