<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atomfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="0.3">

<title type="text/plain">Just a Theory</title>
<tagline type="text/plain">Theory waxes practical. By David E. Wheeler.</tagline>
<link rel="alternate" type="text/html" href="http://justatheory.com" />
<id>tag:justatheory.com,2012:/</id>
<generator url="http://www.blosxom.com/" version="2.1">Blosxom</generator>

<link rel="start" type="application/atom+xml" href="http://feeds.feedburner.com/justatheory/atomfull" /><feedburner:info uri="justatheory/atomfull" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
<id>tag:justatheory.com,2012:/computers/databases/dbi-in-sqitch</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/L2fS5_mX_GU/dbi-in-sqitch.html" />
<title type="text/plain">Use of DBI in Sqitch</title>
<dc:subject>Perl</dc:subject>
<dc:subject>DBI</dc:subject>
<dc:subject>Sqitch</dc:subject>
<dc:subject>dependencies</dc:subject>
<dc:subject>IPC</dc:subject>
<issued>2012-05-15T17:41:00Z</issued>
<modified>2012-05-15T17:41:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="text/html" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="escaped">
&lt;p&gt;&lt;a href="http://sqitch.org/"&gt;Sqitch&lt;/a&gt; uses the native database client applications (&lt;a href="http://www.postgresql.org/docs/current/static/app-psql.html"&gt;&lt;code&gt;psql&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://man.he.net/man1/sqlite3"&gt;&lt;code&gt;sqlite3&lt;/code&gt;&lt;/a&gt;, &lt;a href="http://dev.mysql.com/doc/refman/5.5/en/mysql.html"&gt;&lt;code&gt;mysql&lt;/code&gt;&lt;/a&gt;, etc.). So for tracking metadata about the state of deployments, I have been trying to stick to using them. I&amp;rsquo;m first targeting PostgreSQL, and as a result need to open a connection to &lt;code&gt;psql&lt;/code&gt;, start a transaction, and be able to read and write stuff to it as migrations go along. &lt;a href="http://stackoverflow.com/questions/10569805/what-is-the-preferred-cross-platform-ipc-perl-module"&gt;The IPC&lt;/a&gt; is a &lt;a href="http://www.perlmonks.org/?node_id=970244"&gt;huge PITA&lt;/a&gt;. Furthermore, getting things properly quoted is also pretty annoying &amp;mdash; and it will be worse for SQLite and MySQL, I expect (&lt;code&gt;psql&lt;/code&gt;&amp;rsquo;s &lt;code&gt;--set&lt;/code&gt; support is pretty slick).&lt;/p&gt;

&lt;p&gt;If, on the other hand, I used the &lt;a href="https://metacpan.org/module/DBI"&gt;DBI&lt;/a&gt;, on the other hand, all this would be very easy. There is no IPC, just a direct connection to the database. It would save me a ton of time doing development, and be robust and safer to use (e.g., exception handling rather than platform-dependent signal handling (or not, in the case of Windows)). I am quite tempted to just so that.&lt;/p&gt;

&lt;p&gt;However, I have been trying to be sensitive to dependencies. I had planned to make Sqitch simple to install on any system, and if you had the command-line client for your preferred database, it would just work. If I used the DBI instead, then Sqitch would not work at all unless you installed the appropriate DBI driver for your database of choice. This is no big deal for Perl people, of course, but I don&amp;rsquo;t want this to be a Perl people tool. I want it to be dead simple for anyone to use for any database. Ideally, there will be RPMs and Ubuntu packages, so one can just install it and go, and not have to worry about figuring out what additional Perl DBD to install for your database of choice. It should be transparent.&lt;/p&gt;

&lt;p&gt;That is still my goal, but at this point the IPC requirements for controlling the clients is driving me a little crazy. Should I just give up and use the DBI (at least for now)? Or persevere with the IPC stuff and get it to work? Opinions wanted!&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/L2fS5_mX_GU" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://justatheory.com/computers/databases/dbi-in-sqitch.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/databases/sqitch-steps</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/DeC0DdLk4ZM/sqitch-steps.html" />
<title type="text/plain">Sqitch Status: A Step at a Time</title>
<dc:subject>sqitch</dc:subject>
<dc:subject>sql+change+management</dc:subject>
<dc:subject>sql</dc:subject>
<dc:subject>add-step</dc:subject>
<issued>2012-05-01T04:09:00Z</issued>
<modified>2012-05-01T04:09:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I've just released <a href="http://search.cpan.org/dist/App-Sqitch-0.20-TRIAL/">Sqitch v0.20-TRIAL</a>, the third testing release of Sqitch. Since last week, I've implemented <a href="http://search.cpan.org/dist/App-Sqitch-0.20-TRIAL/lib/sqitch-add-step.pod"><code>add-step</code></a>. So let's have a look-see at what all it can do. First, let's initialize a Sqitch project.</p>

<pre><code>&gt; mkdir myproj 
&gt; cd myproj 
myproj&gt; git init
Initialized empty Git repository in myproj/.git/
myproj&gt; sqitch --engine pg init
Created sql/deploy
Created sql/revert
Created sql/test
Created ./sqitch.conf
</code></pre>

<p>Doesn't look like much, does it? Let's set the database name and look at the configuration:</p>

<pre><code>myproj&gt; sqitch config core.pg.db_name flipr_test
myproj&gt; less sqitch.conf
[core]
    engine = pg
    # plan_file = sqitch.plan
    # sql_dir = sql
    # deploy_dir = sql/deploy
    # revert_dir = sql/revert
    # test_dir = sql/test
    # extension = sql
# [core "pg"]
    # db_name = 
    # client = psql
    # sqitch_schema = sqitch
    # password = 
    # port = 
    # host = 
    # username = 
[core "pg"]
    db_name = flipr_test
</code></pre>

<p>I've made an effort to make the default configuration file as useful as possible by including all the core and engine settings. Defaults are present, too, but commented-out. Some you'd probably never want to change in the local file, but might in your user file or in the system configuration file. Peruse the <a href="http://search.cpan.org/dist/App-Sqitch-0.20-TRIAL/lib/sqitch-add-step.pod"><code>sqitch-config</code> man page</a> for all the Git-like awesomeness.s</p>

<p>So now we can add a step:</p>

<pre><code>myproj&gt; sqitch add-step user_roles
Created sql/deploy/user_roles.sql
Created sql/revert/user_roles.sql
Created sql/test/user_roles.sql
</code></pre>

<p>Wee! Again, doesn't look like much, I know. But in fact the generated scripts are created from <a href="http://search.cpan.org/perldoc?Template::Tiny">Template::Tiny</a> templates, and again, they can be overridden on a user or system basis. Have a look at the <a href="http://search.cpan.org/dist/App-Sqitch-0.20-TRIAL/lib/sqitch-add-step.pod"><code>add-step</code> man page</a> for the details. Or just start with what's there: edit the generated scripts to deploy and revert your changes. Go crazy. The deploy script looks like this:</p>

<pre><code>myproj&gt; less sql/deploy/user_roles.sql 
-- Deploy user_roles

BEGIN;

-- XXX Add DDLs here.

COMMIT;
</code></pre>

<p>Next up, deployment. I think that will require that the plan interface be written, first. I'll be getting on that tomorrow.</p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/DeC0DdLk4ZM" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/computers/databases/sqitch-steps.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/databases/sqitch-update</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/xqgtGLczivA/sqitch-update.html" />
<title type="text/plain">Sqitch Update</title>

<issued>2012-04-28T04:32:00Z</issued>
<modified>2012-04-28T04:32:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p>A quick update on <a href="https://github.com/theory/sqitch/">Sqitch</a>. I started implementation about a couple of weeks ago. It’s coming a long a bit more slowly than I'd like, given that I need to give <a href="http://www.pgcon.org/2012/schedule/events/479.en.html">a presentation</a> on it soon. But I did things a little differently than I usually do with project like this: I wrote documentation first. In addition to the basic docs I <a href="http://justatheory.com/computers/databases/sqitch-draft.html">posted</a> a couple weeks back, I’ve written <a href="https://github.com/theory/sqitch/blob/master/lib/sqitchtutorial.pod">a tutorial</a>. I put quite a lot of time into it, studying the <a href="http://git-scm.com/">Git</a> interface as I did so, to try to develop useful workflows. The nice thing about this it that it will not only serve as the foundation for my presentation (<em>PHEW!</em> Half the work done already!), but it also serves as a design specification.</p>

<p>So I've been diligently plugging away on it, and have uploaded a couple of trial releases <a href="http://search.cpan.org/dist/App-Sqitch/">to CPAN</a>. So far, we have decent support for:</p>

<ul>
<li><code>sqitch help</code> and <code>sqitch help command</code>. The latter only works for the implemented commands, of course.</li>
<li><code>sqitch config</code>, which is a near perfect duplication of <a href="http://www.gitmanual.org/git-config.html"><code>git-config</code></a>, thanks to the very useful <a href="https://metacpan.org/module/Config::GitLike/">Config::GitLike</a>. It supports a local, project-specific config file, a user config file, and a system config file.</li>
<li><code>sqitch init</code>, which creates a new project by creating directories for the deploy, revert, and test scripts, and writes a  project-specific config file. This file has options you specify in the call to <code>sqitch</code> (such as the database engine you plan to use), and all unmodified settings or settings set in user or system configuration are written out as comments.</li>
</ul>

<p>So yeah, not a ton so far, but the foundations for how it all goes together are there, so it should take less time to develop other commands, all things being equal.</p>

<p>Next up:</p>

<ul>
<li><code>sqitch add-step</code>, which will create deploy and revert scripts for a new step, based on simple templates.</li>
<li><code>sqitch deploy</code>, which is the big one. Initial support will be there for PostgreSQL and SQLite (and perhaps MySQL).</li>
</ul>

<p>Interested in helping out?</p>

<ul>
<li><p>I'm going to need a parser for <a href="https://github.com/theory/sqitch/blob/master/lib/sqitch.pod#plan-file">the plan file</a> pretty soon. The interface will need an iterator to move back and forth in the file, as well as a way to write to the file, add steps to it, etc. The <a href="https://github.com/theory/sqitch/blob/master/lib/sqitch.pod#grammar">grammar</a> is pretty simple, so anyone familiar with parsers and iterators could probably knock something out pretty quickly.</p></li>
<li><p>The interface for testing needs some thinking through. I had been thinking that it could be something as simple as just diffing the output of a script file against an expected output file, at least to start. One could even use <a href="http://pgtap.org/">pgTAP</a> or <a href="http://theory.github.com/mytap/">MyTAP</a> in such scripts, although it might be a pain to get the output exactly right for varying environments. But maybe that doesn't matter for deployment, so much? Because it tends to be to a more controlled environment than your typical open-source library test suite, I mean.</p></li>
</ul>

<p>Got something to add? <a href="https://github.com/theory/sqitch">Fork it!</a></p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/xqgtGLczivA" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/computers/databases/sqitch-update.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/travel/france/2012/rouen-again</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/15-kDjKsskk/rouen-again.html" />
<title type="text/plain">Rouen Again</title>
<dc:subject>travel</dc:subject>
<dc:subject>France</dc:subject>
<dc:subject>Rouen</dc:subject>
<dc:subject>Normandy</dc:subject>
<issued>2012-04-27T04:07:00Z</issued>
<modified>2012-04-27T04:07:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p><a href="http://www.flickr.com/photos/theory/3330418728/"><img src="http://farm4.staticflickr.com/3359/3330418728_7a8a5024bb_z.jpg?zz=1" alt="Rouen Skyline" title="" /></a></p>

<p>A few years back, we spent six weeks in Rouen, France. I <a href="http://justatheory.com/travel/france/">blogged about it a little</a> and took <a href="http://www.flickr.com/photos/theory/tags/france/">a lot of photos</a>. It was a terrific experience, and we always knew we wanted to do it again. So you can imagine our delight at being asked to swap houses with the same folks again. Last time it was late November to early January: cold, cold, cold. This time, though, it will be the middle of summer. We leave mid-June and will return in early August. Seven weeks of awesomeness. Can't wait.</p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/15-kDjKsskk" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/travel/france/2012/rouen-again.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/culture/journalism/open-letter-printed-press</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/cPi_SLviRYk/open-letter-printed-press.html" />
<title type="text/plain">An open letter to the printed press</title>
<dc:subject>publishing</dc:subject>
<dc:subject>content</dc:subject>
<dc:subject>periodicals</dc:subject>
<dc:subject>magazines</dc:subject>
<dc:subject>newspapers</dc:subject>
<dc:subject>iPad</dc:subject>
<dc:subject>tablets</dc:subject>
<dc:subject>jobs+to+do</dc:subject>
<dc:subject>disruption</dc:subject>
<issued>2012-04-23T04:26:00Z</issued>
<modified>2012-04-23T04:26:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Last weekend, I was talking to a friend who works for a moderately-sized regional newspaper. We were talking about distribution. The guys upstairs, he told me, feel very comfortable, complacent even, with their position. They’re not worried about their future because, as they say, “Hey, we’re not the <em>New York Times,</em> we’re not going to have those kind of distribution problems, because we cover regional news, and there’s still a need for good regional coverage.” I guess they think that people in their region don’t read local news on the internet or their mobile devices?</p>

<p>This is so incredibly wrong-headed it boggles the mind. This complacency will kill a perfectly good regional news source, all because the folks in charge are so blinkered that they cannot see that distribution is about to undergo a disruption not seen since…hell, I don’t know when.</p>

<p>I am a great believer in quality. There will always be a place for good writing, good editing, and solid reportage. We need organizations to employ journalists to investigate goings-on and report on them. There needs to be fact-checking, copy editing, compelling photography and illustration, and most of all, people who are willing to dig, to dig up the truth and tell stories that inform us, challenge us, and yes, entertain us.</p>

<p>Now, tell me, where in this characterization do you see any mention of forests of paper, barrels of inks, and warehouses of printing presses?</p>

<p>The printed press has struggled with the change to internet distribution over the last 15 years with very good reason: It’s difficult to make money. It didn’t help that so many of them gave away their content. But that change is nothing compared to the revolution that is the tablet computer, and especially the new iPad. This is a device you can take <em>anywhere,</em> and unlike your phone, is a pleasure to <em>read.</em> It’s as easy to take with you as a newspaper or magazine, but offers so much more. It’s here to stay. And it’s going to kill the printing press.<a href="#fn:niche" id="fnref:niche" class="footnote">1</a></p>

<p>Some disagree. An iPad does not offer the same pleasures as a newspaper: the texture of the pages, the scanability of the front page, the smell of the ink and the smudges it leaves on your fingers. No, there is nothing like a Sunday <em>Times,</em> a bagel, and a cup of coffee to laze away the morning. And when you’re done with the paper, the way it’s strewn about, the poorly refolded pages and crumpled edges of the most interesting sections offer satisfying remains of the experience. And then you recycle it.</p>

<p>Try that with your iPad. Don’t want to get cream cheese on it, or spill your coffee. Its scent does not bring back the memory of lazy Sundays, it doesn’t smudge your fingers, it doesn’t get crumpled or leave behind any of the detritus indicating a satisfying read. Just a smudged up screen, which won’t be nostalgic to anyone.</p>

<p>At the same time, you can’t perform a full text search of your newspaper. You can’t go back and read the article from last week because the recycling has been picked up. You can’t zoom in to a newspaper photo to look at things in greater detail. You can’t make text larger to relieve your aging eyes, or dig deeper to find out the story behind the photograph on the front page.</p>

<p>Look here, periodicals companies. This is important. It’s time realized that you are <em>not</em> in the printing business. You are in either the content business or the advertising business. You either sell your content to consumers in ways that are easy or enjoyable for them to access, or get your ads in front of as many eyeballs as possible (or both). The way to do that is not to run printing presses. Nor is it to squeeze the disadvantages of the printed page on devices. The way to do it is to provide the best experience possible. Decide what methods of distribution you want to use—print, web, tablet—and <em>take advantage of the strengths and weaknesses of each to make things people want.</em></p>

<p>So yeah, keep printing, for now, to satisfy the aging population that needs it. Make the experience the best you can within the constraints of the printed page. But don’t force those same constraints into another distribution channel. You don’t try to make the printed page look and feel like a scrolling screen, do you? Nor should you make the iPad experience feel like the printed page. No, I can’t smell the ink in your iPad app, but if you took proper advantage of the device, tried to work within the confines of its limitations while exploiting is unique strengths, you could provide a compelling, unique experience.</p>

<p>Because if you don’t, someone else will. The iPad in particular represents a gaping opportunity for disruption of your business, mainly because you fail to recognize that you are in the content business, not the distribution business. And upstart companies will start delivering well researched, edited, and fact-checked stories in a compelling format, with new approaches to interaction and engagement, in a way that people <em>want.</em> And they will be extremely successful. And profitable.</p>

<div class="footnotes">
<hr />
<ol>

<li id="fn:niche"><p>Okay, not kill it, exactly, but turn printing into a niche business, suitable for coffee table books, wedding invitations and book arts.<a href="#fnref:niche" class="reversefootnote"> ↩</a></p></li>

</ol>
</div><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/cPi_SLviRYk" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/culture/journalism/open-letter-printed-press.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/databases/postgresql/use-timestamptz</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/yPH6nuTQ2mw/use-timestamptz.html" />
<title type="text/plain">Always Use TIMESTAMP WITH TIME ZONE</title>
<dc:subject>time+zone</dc:subject>
<dc:subject>timezone</dc:subject>
<dc:subject>timestamp</dc:subject>
<dc:subject>timestmaptz</dc:subject>
<dc:subject>time</dc:subject>
<dc:subject>timetz</dc:subject>
<dc:subject>database</dc:subject>
<dc:subject>PostgreSQL</dc:subject>
<dc:subject>best+practice</dc:subject>
<dc:subject>recommendations</dc:subject>
<issued>2012-04-16T22:08:00Z</issued>
<modified>2012-04-16T22:08:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p>My recommendations for sane time zone management in PostgreSQL:</p>

<ul>
<li>Set <code>timezone = 'UTC'</code> in <code>postgresq.conf</code>. This makes UTC the default time zone for all connections.</li>
<li>Use <a href="http://www.postgresql.org/docs/current/static/datatype-datetime.html"><code>timestamp with time zone</code> (aka <code>timestamptz</code>) and <code>time with time zone</code> (aka <code>timetz</code>)</a>. They store values as UTC, but convert them on selection to whatever your time zone setting is.</li>
<li>Avoid <code>timestamp without time zone</code> (aka <code>timestamp</code>) and <code>time without time zone</code> (aka <code>time</code>). These columns do not know the time zone of a value, so different apps can insert values in different zones no one would ever know.</li>
<li>Always specify a time zone when inserting into a <code>timestamptz</code> or <code>timetz</code> column. Unless the zone is UTC. But even then, append a "Z" to your value: it's more explicit, and will keep you sane.</li>
<li>If you need to get <code>timestamptz</code> or <code>timetz</code> values in a zone other than UTC, use the <a href="http://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT"><code>AT TIME ZONE</code> expression in your query</a>. But be aware that the returned value will be a <code>timestamp</code> or <code>time</code> value, with no more time zone. Good for reporting and queries, bad for storage.</li>
<li>If your app <em>always</em> needs data in some other time zone, have it <a href="http://www.postgresql.org/docs/9.1/static/runtime-config-client.html#GUC-TIMEZONE"><code>SET timezone = 'UTC'</code></a> on connection. All values then retrieved from the database will be in the configured time zone. The app should still include the time zone in values sent to the database.</li>
</ul>

<p>The one exception to the rule preferring <code>timestamptz</code> and <code>timetz</code> is a special case: partitioning. When partitioning data on timestamps, you <em>must not</em> use <code>timestamptz</code>. Why? Because almost no expression involving <code>timestamptz</code> comparison is immutable. Use one in a <code>WHERE</code> clause, and <a href="http://www.postgresql.org/docs/9.1/static/ddl-partitioning.html#DDL-PARTITIONING-CONSTRAINT-EXCLUSION">constraint exclusion</a> may well <a href="http://comments.gmane.org/gmane.comp.db.postgresql.performance/29681">be ignored</a> and all partitions scanned. This is usually something you want to avoid.</p>

<p>So in <strong>this one case</strong> and <strong>only in this one case</strong>, use a <code>timestamp without time zone</code> column, but <em>always insert data in UTC</em>. This will keep things consistent with the <code>timestamptz</code> columns you have everywhere else in your database. Unless your app changes the value of the <a href="http://www.postgresql.org/docs/9.1/static/runtime-config-client.html#GUC-TIMEZONE"><code>timestamp</code> GUC</a> when it connects, it can just assume that everything is always UTC, and should always send updates as UTC.</p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/yPH6nuTQ2mw" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/computers/databases/postgresql/use-timestamptz.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/blog/new-home</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/E_DZnRKs6IM/new-home.html" />
<title type="text/plain">New Home</title>
<dc:subject>blog</dc:subject>
<dc:subject>blogging</dc:subject>
<dc:subject>blosxom</dc:subject>
<dc:subject>fireballed</dc:subject>
<dc:subject>static</dc:subject>
<issued>2012-04-10T05:52:00Z</issued>
<modified>2012-04-10T05:52:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p>After
<a href="/computers/blog/how-not-to-withstand-a-fireballing.html">the Fireballing</a>
week before last, I put aside a bit of time to rejigger things. This blog
now has a new home.</p>

<ul>
<li>Moved all content to a <a href="http://www.linode.com/">Linode</a> virtual server. No more serving from my crappy old desktop system behind my Comcast connection. The VPS is kind of skimpy on the RAM, but seems fine for my basic needs.</li>
<li>Still using <a href="http://blosxom.sourceforge.net/">Blosxom</a>, but all content is statically-generated.</li>
<li>Switched to <a href="http://www.nginx.org/">Nginx</a>. It's fast. Especially for a 100% static site.</li>
<li>Search is gone. No one used it, anyway. That's what <a href="http://duckduckgo.com/">Duck Duck Go</a> is for.</li>
<li>Comments are gone, sort of. I removed the plugin for adding comments to posts. Existing comments are still shown, though.</li>
<li>Added <a href="http://disqus.com/">Disqus</a> commenting. The upshot is that, for the first time in years, one can comment on <em>any</em> post at <em>any</em> time. No more closing comments after two weeks.</li>
<li>Got rid of the "sociable" junk. No one needs hand-holding for sharing, and very few sharing sites are relevant anymore, anyway.</li>
</ul>

<p>Oh, and I also moved <a href="http://www.strongrrl.com/">strongrrl.com</a>, <a href="http://www.kineticode.com/">kineticode.com</a>, and my <a href="http://pgxn.justatheory.com/">PGXN mirror</a> to the Linode host. They're all static, too, so everything is nice and peppy.</p>

<p>So that's step 1. It's enough that I can get back to posting stuff and, on the off chance that I get Fireballed again, I <em>think</em> things will hold up (a simple <a href="http://httpd.apache.org/docs/2.2/programs/ab.html">ab</a> test shows pretty good throughput at about 100 requests/second at a concurrency of 100). Over the next few months, I have other plans:</p>

<ul>
<li>Throw up a new <a href="http://www.kineticode.com/">kineticode.com</a>. The company has actually shut down, so I need to put up a new page to direct interested parties elsewhere.</li>
<li>Redesign Just a Theory. This design was okay in 2004, but never very forward-looking. I want to vastly simplify things. Just down to the bare essentials, really. Be prepared for more junk to disappear.</li>
<li>Move to a new blog engine. Blosxom is okay, but finicky. There are a lot of steps to publishing a post, most of them involving SCP and SSH. I just want to write to a directory to do stuff, and support drafts and whatnot.</li>
</ul>

<p>That last task is the one I'm least likely to find a lot of time to work on, though, as I am already overcommitted to <a href="http://www.iovation.com/">numerous</a> <a href="http://pgxn.org/">other</a> <a href="http://www.designsceneapp.com/">things</a>, and thinking of <a href="http://github.com/theory/sqitch">new</a> <a href="/computers/databases/postgresql/dbix-connector-and-ssi.html">stuff</a> all the time. But I'd really like to make things much nicer for myself, so we'll see.</p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/E_DZnRKs6IM" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/computers/blog/new-home.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/databases/sqitch-draft</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/bgqPM3XvOpA/sqitch-draft.html" />
<title type="text/plain">Sqitch - VCS-powered SQL Change Management</title>
<dc:subject>SQL</dc:subject>
<dc:subject>Sqitch</dc:subject>
<dc:subject>migration</dc:subject>
<dc:subject>deployment</dc:subject>
<dc:subject>reversion</dc:subject>
<dc:subject>database</dc:subject>
<dc:subject>VCS</dc:subject>
<dc:subject>Git</dc:subject>
<issued>2012-04-06T02:08:00Z</issued>
<modified>2012-04-06T02:08:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Back in January, I <a href="/computers/databases/simple-sql-change-management.html">wrote</a> <a href="/computers/databases/vcs-sql-change-management.html">three</a> <a href="/computers/databases/sql-change-management-sans-redundancy.html">posts</a> outlinining some ideas I had about a straight-forward, sane way of managing SQL change managment. The idea revolved around specifying scripts to deploy and revert in a plan file, and generating that plan file from VCS history. I still feel pretty good about the ideas there, and <a href="http://iovation.com/">work</a> has agreed to let me write it and open-source it. Here is the first step making it happen. I call it “Sqitch.”</p>

<p>Why “Sqitch”? Think of it as SQL changes with Git stuck in the middle. Of course I expect to support VCSs other than Git (probably Subversion and Mercurial, though I am not sure yet), but since Git is what I now have the most familiarity with, I thought it kind of fun to kind of reference a VCS in the name, if only obliquely.</p>

<p>This week, I <a href="https://github.com/theory/sqitch">started work on it</a>. My first task is to outline a draft for the interface. Sqitch will be a command-line tool, primarily. The remainder of this post contains the documentation for the draft interface. Thoughts and feedback would be greatly appreciated, especially if you think I've overlooked anything! I do want to keep features pretty minimal for now, though, to build up a solid core to be built on later. But other than that, your criticism is greatly desired.</p>

<p>Next up, I will probably write a tutorial, just so I can make my way through some real-life(ish) examples and notice if I missed anything else. Besides, <a href="http://www.pgcon.org/2012/schedule/events/479.en.html">I'm going to need the tutorial myself</a>! Watch for that next week.</p>

<p>Thanks!</p>

<hr />


<h3 id="Name">Name</h3>

<p>Sqitch - VCS-powered SQL change management</p>

<h3 id="Synopsis">Synopsis</h3>

<pre><code>sqitch [&lt;options&gt;] &lt;command&gt; [&lt;command-options&gt;] [&lt;args&gt;]</code></pre>

<h3 id="Description">Description</h3>

<p>Sqitch is a VCS-aware SQL change management application. What makes it different from your typical <a href="http://search.cpan.org/perldoc?Module::Build::DB">migration</a>-<a href="http://search.cpan.org/perldoc?DBIx::Migration">style</a> approaches? A few things:</p>

<dl>

<dt>No opinions</dt>
<dd>

<p>Sqitch is not integrated with any framework, ORM, or platform. Rather, it is a standalone change management system with no opinions on your database or development choices.</p>

</dd>
<dt>Native scripting</dt>
<dd>

<p>Changes are implemented as scripts native to your selected database engine. Writing a <a href="http://postgresql.org/">PostgreSQL</a> application? Write SQL scripts for <a href="http://www.postgresql.org/docs/current/static/app-psql.html"><code>psql</code></a>. Writing a <a href="http://mysql.com/">MySQL</a>-backed app? Write SQL scripts for <a href="http://dev.mysql.com/doc/refman/5.6/en/mysql.html"><code>mysql</code></a>.</p>

</dd>
<dt>VCS integration</dt>
<dd>

<p>Sqitch likes to use your VCS history to determine in what order to execute changes. No need to keep track of execution order, your VCS already tracks information sufficient for Sqitch to figure it out for you.</p>

</dd>
<dt>Dependency resolution</dt>
<dd>

<p>Deployment steps can declare dependencies on other deployment steps. This ensures proper order of execution, even when you've committed changes to your VCS out-of-order.</p>

</dd>
<dt>No numbering</dt>
<dd>

<p>Change deployment is managed either by maintaining a plan file or, more usefully, your VCS history. As such, there is no need to number your changes, although you can if you want. Sqitch does not care what you name your changes.</p>

</dd>
<dt>Packaging</dt>
<dd>

<p>Using your VCS history for deployment but need to ship a tarball or RPM? Easy, just have Sqitch read your VCS history and write out a plan file with your change scripts. Once deployed, Sqitch can use the plan file to deploy the changes in the proper order.</p>

</dd>
<dt>Reduced Duplication</dt>
<dd>

<p>If you're using a VCS to track your changes, you don't have to duplicate entire change scripts for simple changes. As long as the changes are <a href="http://en.wikipedia.org/wiki/Idempotence">idempotent</a>, you can change your code directly, and Sqitch will know it needs to be updated.</p>

</dd>
</dl>

<h4 id="Terminology">Terminology</h4>

<dl>

<dt><code>step</code></dt>
<dd>

<p>A named unit of change. A step name must be used in the file names of its corresponding deployment and a reversion scripts. It may also be used in a test script file name.</p>

</dd>
<dt><code>tag</code></dt>
<dd>

<p>A known deployment state with a list one or more steps that define the tag. A tag also implies that steps from previous tags in the plan have been applied. Think of it is a version number or VCS revision. A given point in the plan may have one or more tags.</p>

</dd>
<dt><code>state</code></dt>
<dd>

<p>The current state of the database. This is represented by the most recent tag or tags deployed. If the state of the database is the same as the most recent tag, then it is considered "up-to-date".</p>

</dd>
<dt><code>plan</code></dt>
<dd>

<p>A list of one or more tags and associated steps that define the order of deployment execution. Sqitch reads the plan to determine what steps to execute to change the database from one state to another. The plan may be represented by a <a href="#Plan-File">"Plan File"</a> or by VCS history.</p>

</dd>
<dt><code>deploy</code></dt>
<dd>

<p>The act of deploying database changes to reach a tagged deployment point. Sqitch reads the plan, checks the current state of the database, and applies all the steps necessary to change the state to the specified tag.</p>

</dd>
<dt><code>revert</code></dt>
<dd>

<p>The act of reverting database changes to reach an earlier tagged deployment point. Sqitch checks the current state of the database, reads the plan, and applies reversion scripts for all steps to return the state to an earlier tag.</p>

</dd>
</dl>

<h3 id="Options">Options</h3>

<pre><code>-p --plan-file  FILE    Path to a deployment plan file.
-e --engine     ENGINE  Database engine.
-c --client     PATH    Path to the engine command-line client.
-d --db-name    NAME    Database name.
-u --username   USER    Database user name.
-h --host       HOST    Database server host name.
-n --port       PORT    Database server port number.
   --sql-dir    DIR     Path to directory with deploy and revert scripts.
   --deploy-dir DIR     Path to directory with SQL deployment scripts.
   --revert-dir DIR     Path to directory with SQL reversion scripts.
   --test-dir   DIR     Path to directory with SQL test scripts.
   --extension  EXT     SQL script file name extension.
   --dry-run            Execute command without making any changes.
-v --verbose            Increment verbosity.
-V --version            Print the version number and exit.
-H --help               Print a usage statement and exit.
-M --man                Print the complete documentation and exit.</code></pre>

<h3 id="Options-Details">Options Details</h3>

<dl>

<dt><code>-p</code></dt>
<dd>

</dd>
<dt><code>--plan-file</code></dt>
<dd>

<pre><code>sqitch --plan-file plan.conf
sqitch -p sql/deploy.conf</code></pre>

<p>Path to the deployment plan file. Defaults to <i>./sqitch.plan</i>. If this file is not present, Sqitch will attempt to read from VCS files. If no supported VCS system is in place, an exception will be thrown. See <a href="#Plan-File">"Plan File"</a> for a description of its structure.</p>

</dd>
<dt><code>-e</code></dt>
<dd>

</dd>
<dt><code>--engine</code></dt>
<dd>

<pre><code>sqitch --engine pg
sqitch -e sqlite</code></pre>

<p>The database engine to use. Supported engines include:</p>

<ul>

<li><p><code>pg</code> - <a href="http://postgresql.org/">PostgreSQL</a></p>

</li>
<li><p><code>mysql</code> - <a href="http://mysql.com/">MySQL</a></p>

</li>
<li><p><code>sqlite</code> - <a href="http://sqlite.org/">SQLite</a></p>

</li>
</ul>

</dd>
<dt><code>-c</code></dt>
<dd>

</dd>
<dt><code>--client</code></dt>
<dd>

<pre><code>sqitch --client /usr/local/pgsql/bin/psql
sqitch -c /usr/bin/sqlite3</code></pre>

<p>Path to the command-line client for the database engine. Defaults to a client in the current path named appropriately for the specified engine.</p>

</dd>
<dt><code>-d</code></dt>
<dd>

</dd>
<dt><code>--db-name</code></dt>
<dd>

<p>Name of the database. For some engines, such as <a href="http://postgresql.org/">PostgreSQL</a> and <a href="http://mysql.com/">MySQL</a>, the database must already exist. For others, such as <a href="http://sqlite.org/">SQLite</a>, the database will be automatically created on first connect.</p>

</dd>
<dt><code>-u</code></dt>
<dd>

</dd>
<dt><code>--user</code></dt>
<dd>

</dd>
<dt><code>--username</code></dt>
<dd>

<p>User name to use when connecting to the database. Does not apply to all engines.</p>

</dd>
<dt><code>-h</code></dt>
<dd>

</dd>
<dt><code>--host</code></dt>
<dd>

<p>Host name to use when connecting to the database. Does not apply to all engines.</p>

</dd>
<dt><code>-n</code></dt>
<dd>

</dd>
<dt><code>--port</code></dt>
<dd>

<p>Port number to connect to. Does not apply to all engines.</p>

</dd>
<dt><code>--sql-dir</code></dt>
<dd>

<pre><code>sqitch --sql-dir migrations/</code></pre>

<p>Path to directory containing deployment, reversion, and test SQL scripts. It should contain subdirectories named <code>deploy</code>, <code>revert</code>, and (optionally) <code>test</code>. These may be overridden by <code>--deploy-dir</code>, <code>--revert-dir</code>, and <code>--test-dir</code>. Defaults to <code>./sql</code>.</p>

</dd>
<dt><code>--deploy-dir</code></dt>
<dd>

<pre><code>sqitch --deploy-dir db/up</code></pre>

<p>Path to a directory containing SQL deployment scripts. Overrides the value implied by <code>--sql-dir</code>.</p>

</dd>
<dt><code>--revert-dir</code></dt>
<dd>

<pre><code>sqitch --revert-dir db/up</code></pre>

<p>Path to a directory containing SQL reversion scripts. Overrides the value implied by <code>--sql-dir</code>.</p>

</dd>
<dt><code>--test-dir</code></dt>
<dd>

<pre><code>sqitch --test-dir db/t</code></pre>

<p>Path to a directory containing SQL test scripts. Overrides the value implied by <code>--sql-dir</code>.</p>

</dd>
<dt><code>--extension</code></dt>
<dd>

<pre><code>sqitch --extension ddl</code></pre>

<p>The file name extension on deployment, reversion, and test SQL scripts. Defaults to <code>sql</code>.</p>

</dd>
<dt><code>--dry-run</code></dt>
<dd>

<pre><code>sqitch --dry-run</code></pre>

<p>Execute the Sqitch command without making any actual changes. This allows you to see what Sqitch would actually do, without doing it. Implies a verbosity level of 1; add extra <code>--verbose</code>s for greater verbosity.</p>

</dd>
<dt><code>-v</code></dt>
<dd>

</dd>
<dt><code>--verbose</code></dt>
<dd>

<pre><code>sqitch --verbose -v</code></pre>

<p>A value between 0 and 3 specifying how verbose Sqitch should be. The default is 0, meaning that Sqitch will be silent. A value of 1 causes Sqitch to output some information about what it's doing, while 2 and 3 each cause greater verbosity.</p>

</dd>
<dt><code>-H</code></dt>
<dd>

</dd>
<dt><code>--help</code></dt>
<dd>

<pre><code>sqitch --help
sqitch -H</code></pre>

<p>Outputs a brief description of the options supported by <code>sqitch</code> and exits.</p>

</dd>
<dt><code>-M</code></dt>
<dd>

</dd>
<dt><code>--man</code></dt>
<dd>

<pre><code>sqitch --man
sqitch -M</code></pre>

<p>Outputs this documentation and exits.</p>

</dd>
<dt><code>-V</code></dt>
<dd>

</dd>
<dt><code>--version</code></dt>
<dd>

<pre><code>sqitch --version
sqitch -V</code></pre>

<p>Outputs the program name and version and exits.</p>

</dd>
</dl>

<h3 id="Sqitch-Commands">Sqitch Commands</h3>

<dl>

<dt><code>init</code></dt>
<dd>

<p>Initialize the database and create deployment script directories if they do not already exist.</p>

</dd>
<dt><code>status</code></dt>
<dd>

<p>Output information about the current status of the deployment, including a list of tags, deployments, and dates in chronological order. If any deploy scripts are not currently deployed, they will be listed separately.</p>

</dd>
<dt><code>check</code></dt>
<dd>

<p>Sanity check the deployment scripts. Checks include:</p>

<ul>

<li><p>Make sure all deployment scripts have complementary reversion scripts.</p>

</li>
<li><p>Make sure no deployment script appears more than once in the plan file.</p>

</li>
</ul>

</dd>
<dt><code>deploy</code></dt>
<dd>

<p>Deploy changes. Configuration properties may be specified under the <code>[deploy]</code> section of the configuration file, or via <code>sqitch config</code>:</p>

<pre><code>sqitch config deploy.$property $value</code></pre>

<p>Options and configuration properties:</p>

<dl>

<dt><code>--to</code></dt>
<dd>

<p>Tag to deploy up to. Defaults to the latest tag or to the VCS <code>HEAD</code> commit. Property name: <code>deploy.to</code>.</p>

</dd>
</dl>

</dd>
<dt><code>revert</code></dt>
<dd>

<p>Revert changes. Configuration properties may be specified under the <code>[revert]</code> section of the configuration file, or via <code>sqitch config</code>:</p>

<pre><code>sqitch config revert.$property $value</code></pre>

<p>Options and configuration properties:</p>

<dl>

<dt><code>--to</code></dt>
<dd>

<p>Tag to revert to. Defaults to reverting all changes. Property name: <code>revert.to</code>.</p>

</dd>
</dl>

</dd>
<dt><code>test</code></dt>
<dd>

<p>Test changes. All SQL scripts in <code>--test-dir</code> will be run. [XXX Not sure whether to have subdirectories for tests and expected output and to diff them, or to use some other approach.]</p>

</dd>
<dt><code>config</code></dt>
<dd>

<p>Set configuration options. By default, the options will be written to the local configuration file, <i>sqitch.ini</i>. Options:</p>

<dl>

<dt><code>--get</code></dt>
<dd>

<p>Get the value for a given key. Returns error code 1.</p>

</dd>
<dt><code>--unset</code></dt>
<dd>

<p>Remove the line matching the key from config file.</p>

</dd>
<dt><code>--list</code></dt>
<dd>

<p>List all variables set in config file.</p>

</dd>
<dt><code>--global</code></dt>
<dd>

<p>For writing options: write to global <i>~/.sqitch/config.ini</i> file rather than the local <i>sqitch.ini</i>.</p>

<p>For reading options: read only from global <i>~/.sqitch/config.ini</i> rather than from all available files.</p>

</dd>
<dt><code>--system</code></dt>
<dd>

<p>For writing options: write to system-wide <i>$prefix/etc/sqitch.ini</i> file rather than the local <i>sqitch.ini</i>.</p>

<p>For reading options: read only from system-wide <i>$prefix/etc/sqitch.ini</i> rather than from all available files.</p>

</dd>
<dt><code>--config-file</code></dt>
<dd>

<p>Use the given config file.</p>

</dd>
</dl>

</dd>
<dt><code>package</code></dt>
<dd>

<p>Package up all deployment and reversion scripts and write out a plan file. Configuration properties may be specified under the <code>[package]</code> section of the configuration file, or via <code>sqitch config package.$property $value</code> command. Options and configuration properties:</p>

<dl>

<dt><code>--from</code></dt>
<dd>

<p>Tag to start the plan from. All tags and steps prior to that tag will not be included in the plan, and their change scripts Will be omitted from the package directory. Useful if you've rejiggered your deployment steps to start from a point later in your VCS history than the beginning of time. Property name: <code>package.from</code>.</p>

</dd>
<dt><code>--to</code></dt>
<dd>

<p>Tag with which to end the plan. No steps or tags after that tag will be included in the plan, and their change scripts will be omitted from the package directory. Property name: <code>package.to</code>.</p>

</dd>
<dt><code>--tags-only</code></dt>
<dd>

<p>Write the plan file with deployment targets listed under VCS tags, rather than individual commits. Property name: <code>package.tags_only</code>.</p>

</dd>
<dt><code>--destdir</code></dt>
<dd>

<p>Specify a destination directory. The plan file and <code>deploy</code>, <code>revert</code>, and <code>test</code> directories will be written to it. Defaults to "package". Property name: <code>package.destdir</code>.</p>

</dd>
</dl>

</dd>
</dl>

<h3 id="Configuration">Configuration</h3>

<p>Sqitch configuration information is stored in standard <code>INI</code> files. The <code>#</code> and <code>;</code> characters begin comments to the end of line, blank lines are ignored.</p>

<p>The file consists of sections and properties. A section begins with the name of the section in square brackets and continues until the next section begins. Section names are not case sensitive. Only alphanumeric characters, <code>-</code> and <code>.</code> are allowed in section names. Each property must belong to some section, which means that there must be a section header before the first setting of a property.</p>

<p>All the other lines (and the remainder of the line after the section header) are recognized as setting properties, in the form <code>name = value</code>. Leading and trailing whitespace in a property value is discarded. Internal whitespace within a property value is retained verbatim.</p>

<p>All sections are named for commands except for one, named "core", which contains core configuration properties.</p>

<p>Here's an example of a configuration file that might be useful checked into a VCS for a project that deploys to PostgreSQL and stores its deployment scripts with the extension <i>ddl</i> under the <code>migrations</code> directory. It also wants packages to be created in the directory <i>_build/sql</i>, and to deploy starting with the "gamma" tag:</p>

<pre><code>[core]
    engine    = pg
    db        = widgetopolis
    sql_dir   = migrations
    extension = ddl

[revert]
    to        = gamma

[package]
    from      = gamma
    tags_only = yes
    dest_dir  = _build/sql</code></pre>

<h4 id="Core-Properties">Core Properties</h4>

<p>This is the list of core variables, which much appear under the <code>[core]</code> section. See the documentation for individual commands for their configuration options.</p>

<dl>

<dt><code>plan_file</code></dt>
<dd>

<p>The plan file to use. Defaults to <i>sqitch.ini</i> or, if that does not exist, uses the VCS history, if available.</p>

</dd>
<dt><code>engine</code></dt>
<dd>

<p>The database engine to use. Supported engines include:</p>

<ul>

<li><p><code>pg</code> - <a href="http://postgresql.org/">PostgreSQL</a></p>

</li>
<li><p><code>mysql</code> - <a href="http://mysql.com/">MySQL</a></p>

</li>
<li><p><code>sqlite</code> - <a href="http://sqlite.org/">SQLite</a></p>

</li>
</ul>

</dd>
<dt><code>client</code></dt>
<dd>

<p>Path to the command-line client for the database engine. Defaults to a client in the current path named appropriately for the specified engine.</p>

</dd>
<dt><code>db_name</code></dt>
<dd>

<p>Name of the database.</p>

</dd>
<dt><code>username</code></dt>
<dd>

<p>User name to use when connecting to the database. Does not apply to all engines.</p>

</dd>
<dt><code>password</code></dt>
<dd>

<p>Password to use when connecting to the database. Does not apply to all engines.</p>

</dd>
<dt><code>host</code></dt>
<dd>

<p>Host name to use when connecting to the database. Does not apply to all engines.</p>

</dd>
<dt><code>port</code></dt>
<dd>

<p>Port number to connect to. Does not apply to all engines.</p>

</dd>
<dt><code>sql_dir</code></dt>
<dd>

<p>Path to directory containing deployment, reversion, and test SQL scripts. It should contain subdirectories named <code>deploy</code>, <code>revert</code>, and (optionally) <code>test</code>. These may be overridden by <code>deploy_dir</code>, <code>revert_dir</code>, and <code>test_dir</code>. Defaults to <code>./sql</code>.</p>

</dd>
<dt><code>deploy_dir</code></dt>
<dd>

<p>Path to a directory containing SQL deployment scripts. Overrides the value implied by <code>sql_dir</code>.</p>

</dd>
<dt><code>revert_dir</code></dt>
<dd>

<p>Path to a directory containing SQL reversion scripts. Overrides the value implied by <code>sql_dir</code>.</p>

</dd>
<dt><code>test_dir</code></dt>
<dd>

<p>Path to a directory containing SQL test scripts. Overrides the value implied by <code>sql_dir</code>.</p>

</dd>
<dt><code>extension</code></dt>
<dd>

<p>The file name extension on deployment, reversion, and test SQL scripts. Defaults to <code>sql</code>.</p>

</dd>
</dl>

<h3 id="Plan-File">Plan File</h3>

<p>A plan file describes the deployment tags and scripts to be run against a database. In general, if you use a VCS, you probably won't need a plan file, since your VCS history should be able to provide all the information necessary to derive a deployment plan. However, if you really do need to maintain a plan file by hand, or just want to better understand the file as output by the <code>package</code> command, read on.</p>

<h4 id="Format">Format</h4>

<p>The contents of the plan file are plain text encoded as UTF-8. It is divided up into sections that denote deployment states. Each state has a bracketed, space-delimited list of one or more tags to identify it, followed by any number of deployment steps. Here's an example of a plan file with a single state and a single step:</p>

<pre><code>[alpha]
users_table</code></pre>

<p>The state has one tag, named "alpha", and one step, named "users_table". A state may of course have many steps. Here's an expansion:</p>

<pre><code>[root alpha]
users_table
insert_user
update_user
delete_user</code></pre>

<p>This state has two tags, "root" and "alpha", and four steps, "users_table", "insert_user", "update_user", and "delete_user".</p>

<p>Most plans will have multiple states. Here's a longer example with three states:</p>

<pre><code>[root alpha]
users_table
insert_user
update_user
delete_user

[beta]
widgets_table
list_widgets

[gamma]
ftw</code></pre>

<p>Using this plan, to deploy to the "beta" tag, the "root"/"alpha" state steps must be deployed, as must the "beta" steps. To then deploy to the "gamma" tag, the "ftw" step must be deployed. If you then choose to revert to the "alpha" tag, then the "gamma" step ("ftw") and all of the "beta" steps will be reverted in reverse order.</p>

<p>Using this model, steps cannot be repeated between states. One can repeat them, however, if the contents for a file in a given tag can be retrieved from a VCS. An example:</p>

<pre><code>[alpha]
users_table

[beta]
add_widget
widgets_table

[gamma]
add_user

[44ba615b7813531f0acb6810cbf679791fe57bf2]
widgets_created_at

[HEAD epsilon master]
add_widget</code></pre>

<p>This example is derived from a Git log history. Note that the "add_widget" step is repeated under the state tagged "beta" and under the last state. Sqitch will notice the repetition when it parses this file, and then, if it is applying all changes, will fetch the version of the file as of the "beta" tag and apply it at that step, and then, when it gets to the last tag, retrieve the deployment file as of its tags and apply it. This works in reverse, as well, as long as the changes in this file are always <a href="http://en.wikipedia.org/wiki/Idempotence">idempotent</a>.</p>

<h4 id="Grammar">Grammar</h4>

<p>Here is the EBNF Grammar for the plan file:</p>

<pre><code>plan-file   = { &lt;state&gt; | &lt;empty-line&gt; | &lt;comment&gt; }* ;

state       = &lt;tags&gt; &lt;steps&gt; ;

tags        = "[" &lt;taglist&gt; "]" &lt;line-ending&gt; ;
taglist     = &lt;name&gt; | &lt;name&gt; &lt;white-space&gt; &lt;taglist&gt; ;

steps       = { &lt;step&gt; | &lt;empty-line&gt; | &lt;line-ending&gt; }* ;
step        = &lt;name&gt; &lt;line-ending&gt; ;

empty-line  = [ &lt;white-space&gt; ] &lt;line-ending&gt; ;
line-ending = [ &lt;comment&gt; ] &lt;EOL&gt; ;
comment     = [ &lt;white-space&gt; ] "#" [ &lt;string&gt; ] ;

name        = ? non-white space characters ? ;
white-space = ? white space characters ? ;
string      = ? non-EOL characters ? ;</code></pre>

<h3 id="See-Also">See Also</h3>

<p>The original design for Sqitch was sketched out in a number of blog posts:</p>

<ul>

<li><p><a href="http://justatheory.com/computers/databases/simple-sql-change-management.html">Simple SQL Change Management</a></p>

</li>
<li><p><a href="http://justatheory.com/computers/databases/vcs-sql-change-management.html">VCS-Enabled SQL Change Management</a></p>

</li>
<li><p><a href="http://justatheory.com/computers/databases/sql-change-management-sans-redundancy.html">SQL Change Management Sans Duplication</a></p>

</li>
</ul>

<p>Other tools that do database change management include:</p>

<dl>

<dt><a href="http://guides.rubyonrails.org/migrations.html">Rails migrations</a></dt>
<dd>

<p>Numbered migrations for <a href="http://rubyonrails.org/">Ruby on Rails</a>.</p>

</dd>
<dt><a href="http://search.cpan.org/perldoc?Module::Build::DB">Module::Build::DB</a></dt>
<dd>

<p>Numbered changes in pure SQL, integrated with Perl's <a href="http://search.cpan.org/perldoc?Module::Build">Module::Build</a> build system. Does not support reversion.</p>

</dd>
<dt><a href="http://search.cpan.org/perldoc?DBIx::Migration">DBIx::Migration</a></dt>
<dd>

<p>Numbered migrations in pure SQL.</p>

</dd>
<dt><a href="http://www.depesz.com/2010/08/22/versioning/">Versioning</a></dt>
<dd>

<p>PostgreSQL-specific dependency-tracking solution by <a href="http://www.depesz.com/">depesz</a>.</p>

</dd>
</dl>

<h3 id="Author">Author</h3>

<p>David E. Wheeler &lt;david@justatheory.com&gt;</p>

<h3 id="License">License</h3>

<p>Copyright (c) 2012 iovation Inc.</p>

<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>

<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>

<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
<xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/bgqPM3XvOpA" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/computers/databases/sqitch-draft.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/blog/how-not-to-withstand-a-fireballing</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/EHVoTNDxfxA/how-not-to-withstand-a-fireballing.html" />
<title type="text/plain">How Not to Withstand a Fireballing</title>
<dc:subject>meta</dc:subject>
<dc:subject>blog</dc:subject>
<dc:subject>daring+fireball</dc:subject>
<dc:subject>fireballing</dc:subject>
<dc:subject>aaaaauuugh</dc:subject>
<issued>2012-04-04T03:34:00Z</issued>
<modified>2012-04-04T03:34:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><img src="/computers/blog/fireballed.png" alt="Fireballed!" title="Yeah, hovering along at 50-200 hits a day, and then, BAM!" style="float:none" />

<ul>
<li>Set up a simple <a href="http://blosxom.sourceforge.net/">Blosxom</a>-powered blog on the advice of a <a href="http://raelity.org/">friend who wrote it</a></li>
<li>Never bother to make it anything other than a CGI script that crawls your file system on every request</li>
<li>Add a bunch of plugins for various doodads and useless doohickies, also executing a bunch of crap on every request</li>
<li>Put it on your eight year old Dell Dimension running Ubuntu Linux</li>
<li>In your basement</li>
<li>On your residential Comcast connection</li>
<li>Let many years go by, giving it very little attention</li>
<li>Write something interesting</li>
<li>Laugh about your complete inability to connect to the box from work while <a href="http://daringfireball.net/linked/2012/03/29/conde-nast">Daring Fireball melts your connection</a></li>
</ul>

<p>I have more to say on the topic of iPad magazines, but I'm using the meager tuits I have to first make some infrastructural changes.</p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/EHVoTNDxfxA" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/computers/blog/how-not-to-withstand-a-fireballing.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/apps/conde-nast-ipad</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/XkBOHIOwImA/conde-nast-ipad.html" />
<title type="text/plain">Condé Nast on the iPad</title>
<dc:subject>The+New+Yorker</dc:subject>
<dc:subject>Vogue</dc:subject>
<dc:subject>Condé+Nast</dc:subject>
<dc:subject>iPad</dc:subject>
<dc:subject>Retina+display</dc:subject>
<dc:subject>suck</dc:subject>
<issued>2012-03-28T04:46:00Z</issued>
<modified>2012-03-28T04:46:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="text/html" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="escaped">
&lt;p&gt;I've been a &lt;a href="http://www.newyorker.com/"&gt;&lt;em&gt;The New Yorker&lt;/em&gt;&lt;/a&gt; reader for nearly two decades. I'm a huge fan of the magazine, which in my humble has the best reportage anywhere. So I was thrilled last year when the magazine's publisher, &lt;a href="http://www.condenast.com/"&gt;Condé Nast&lt;/a&gt;, decided to make all issues available to paper subscribers for no extra charge. Ever since, I have loved getting the latest issue late on Sunday nights, rather than on the following Friday or Saturday. It has also made it easier for &lt;a href="http://blog.strongrrl.com/"&gt;Strongrrl&lt;/a&gt; and me to share the magazine: I usually read the iPad version and she reads the paper version.&lt;/p&gt;

&lt;p&gt;That's not to say that it has all been happiness and joy, mind. &lt;em&gt;The New Yorker&lt;/em&gt; app is terrible. Issues are &lt;em&gt;huge&lt;/em&gt; (typically around 120 MB). Downloads cannot be performed in the background (except by Newsstand, which may not notice a new issue for days). You can't even read other issues while waiting for one to download; the download pauses. Sometimes I have difficulty getting it to start downloading again. And the navigation, while unique and kind of interesting, really does not work for me.&lt;/p&gt;

&lt;p&gt;Still, I was excited to try it on the new iPad's retina display. Well, "excited" is not quite the right word. More like "dreading." Because Condé iPad magazines actually use images for most of their articles, rather than text. The underlying technology is the charmingly named &lt;a href="http://www.adobe.com/products/digital-publishing-suite-family.html"&gt;Adobe Digital Publishing Suite family&lt;/a&gt;, which, at root, is basically an image reader. Way back in September, 2010, &lt;a href="http://allthingsd.com/20100928/conde-nasts-ipad-apps-are-too-portly-blame-adobe/"&gt;Condé told All Things D&lt;/a&gt;: "The goal is to be all HTML, and we will be." Alas, that day has yet to come, as I confirmed when I loaded the latest issue of &lt;em&gt;The New Yorker&lt;/em&gt; on my new iPad and immediately saw fuzzy text. Sure, the text-based articles, such as the comment, display beautifully. But longer articles, which are carefully laid out and rendered as images, do not. They look worse than on the old iPad, because the anti-aliasing is even easier to see.&lt;/p&gt;

&lt;div class="box"&gt;
&lt;a href="http://www.flickr.com/photos/theory/6861697774/sizes/o/in/set-72157629648560359/" title="The New Yorker on the 1st gen iPad"&gt;&lt;img src="http://farm8.staticflickr.com/7059/6861697774_a7ac0d9356_z.jpg" alt="The New Yorker on the 1st gen iPad" /&gt;&lt;/a&gt;
&lt;a href="http://www.flickr.com/photos/theory/7007813821/sizes/o/in/set-72157629648560359/" alt="The New Yorker on the 3rd gen iPad with retina display"&gt;&lt;img src="http://farm8.staticflickr.com/7110/7007813821_6293e374eb_z.jpg" alt="The New Yorker on the 3rd gen iPad with retina display" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Text content from &lt;em&gt;The New Yorker&lt;/em&gt; on the first generation iPad (top) and the third generation iPad with retina display (bottom). Looks great because it's text. Click for larger images.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;I &lt;a href="https://twitter.com/#!/theory/status/180846743775232000"&gt;complained&lt;/a&gt; about this on Twitter, &lt;a href="https://twitter.com/#!/blam/status/180856508479848448"&gt;Brian Lam chimed in&lt;/a&gt;, and &lt;a href="https://twitter.com/sdadich"&gt;Scott Dadich&lt;/a&gt;, Condé's Vice President of Content Innovation, was kind enough to respond:&lt;/p&gt;

&lt;blockquote class="twitter-tweet" data-in-reply-to="180846743775232000"&gt;&lt;p&gt;@&lt;a href="https://twitter.com/theory"&gt;theory&lt;/a&gt; @&lt;a href="https://twitter.com/blam"&gt;blam&lt;/a&gt; fret not, gents, we're on it. For a preview, check out our first hires magazine with vector text, Vogue.&lt;/p&gt;&amp;mdash; Scott Dadich (@sdadich) &lt;a href="https://twitter.com/sdadich/status/181039871199154177" data-datetime="2012-03-17T15:30:43+00:00"&gt;March 17, 2012&lt;/a&gt;&lt;/blockquote&gt;

&lt;script src="//platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;

&lt;p&gt;I suspect that the reality of the overlap of &lt;em&gt;The New Yorker&lt;/em&gt; and &lt;em&gt;Vogue&lt;/em&gt; readers resembles the Venn diagram to the right. But I want to see &lt;em&gt;The New Yorker&lt;/em&gt; rock on the iPad, so I put aside some time to download the new &lt;em&gt;Vogue&lt;/em&gt; app and take it for a spin.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://farm8.staticflickr.com/7239/7007841235_5559a170e1_o.png" style="width:307pt" alt="The New Yorker &amp;amp; Condé Nast Venn Diagram" title=" The New Yorker &amp;amp; Condé Nast Venn Diagram" /&gt;&lt;/p&gt;

&lt;p&gt;First, the good news. I got a free month's subscription and downloaded the latest issue of &lt;em&gt;Vogue&lt;/em&gt;, which does indeed have hi-res images. The articles look great. Ads are still low-res, but some might consider that a feature (not, I daresay, the advertisers). Alas, that's where the good news ends. Overall, this app is almost exactly like the &lt;em&gt;The New Yorker&lt;/em&gt; app. I find this a little odd, since in print the magazines could not be much less different: &lt;em&gt;The New Yorker&lt;/em&gt; is a slim, staple-bound, mostly-text weekly. &lt;em&gt;Vogue&lt;/em&gt; is a phone book-sized perfect bound, glossy fashion magazine. Quite different beasts. Business-wise, I can understand why they would be the same: It's less expensive to have a single "media player" for all of your company's periodical properties, and the tablet form factor allows you to eliminate some of the differences. After all, bits don't weigh anything.&lt;/p&gt;

&lt;div class="box"&gt;
&lt;a href="http://www.flickr.com/photos/theory/7007813933/sizes/l/in/set-72157629648560359/"&gt;&lt;img src="http://farm8.staticflickr.com/7198/7007813933_bd7e86947c_z.jpg" alt="Vogue on the new iPad" /&gt;&lt;/a&gt;
&lt;p class="caption"&gt;Image content from &lt;em&gt;Vogue&lt;/em&gt; on the new iPad. Not shown: the second that it's blurry while the image engine finishes loading and displaying the image. Click for larger images.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;Except that they do. The &lt;em&gt;Vogue&lt;/em&gt; April 2012 issue weighs in at a whopping 408 MB. A special "exclusive download" covering The Met Gala demands 530 MB of disk space. The latter I can kinda/sorta understand, as it contains a bunch of videos (all low-res text and image content, though). The fact that the new issue is so huge tells me that one of two things, or perhaps both, is going on here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Vogue is such an image-driven magazine that it will just be big no matter what you do.&lt;/li&gt;
&lt;li&gt;The text content is still images, just bigger ones.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll have to wait for the much less image-driven content of &lt;em&gt;The New Yorker&lt;/em&gt; to find out if the its downloads are smaller, but the &lt;em&gt;Vogue&lt;/em&gt; example does not make me optimistic.&lt;/p&gt;

&lt;p&gt;Worse than the download size, though, is the fact that user-triggered downloads do not happen in the background. I started a download and quit the app, then came back after 15 minutes of doing other stuff, and it had gone nowhere, though it restarted the downloading without me needing to do anything more. This is one of the biggest beefs I've had with &lt;em&gt;The New Yorker&lt;/em&gt; app: I have to start a download, and then wait for it to finish, often up to 5 minutes, without being able to do anything else on my iPad. This sucks.&lt;/p&gt;

&lt;p&gt;Other issues I have with the &lt;em&gt;Vogue&lt;/em&gt; app, and which are also present in the existing &lt;em&gt;The New Yorker&lt;/em&gt; app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When not reading an issue, but looking in the "Store" or "My Account," pages are quite slow to load. They appear to be web views that download content every time they are loaded, with no caching. With hi-res images, it gets even slower. It would be nice if these were cached, so that a new download would happen only if there was actually new content.&lt;/li&gt;
&lt;li&gt;Controls can be very slow to respond. I clicked a "Buy Issue" button, and nothing happened for 15 seconds. No activity indicator icon, nothing. Subsequent taps of the button were a bit better, taking only a second or so to respond.&lt;/li&gt;
&lt;li&gt;Not all controls are obvious. For example, in "My Account," the "Complete Account Setup" button is slightly darker than the others, so perhaps disabled. But if I tap it, it depresses. But nothing happens. This is quite different from how standard iOS controls work, where if a button is disabled, its color is greatly reduced and tapping it does nothing.&lt;/li&gt;
&lt;li&gt;When viewing the high-res magazine, images &lt;em&gt;and text&lt;/em&gt; start out low-res, then sharpen. Clearly we are still dealing with image content, even for the text. (&lt;a href="http://reverttosaved.com/2012/03/26/why-do-magazines-look-so-bad-on-the-new-ipad/"&gt;Or perhaps PDFs&lt;/a&gt; and a slow rendering engine. The rendering reminds me of the iterative`` display of progressive JPEGs on the web in the 90s.)&lt;/li&gt;
&lt;li&gt;Some articles have a Chevron icon for different or related content. If you tap one, it jumps to a completely different part of the magazine (think the back pages). There is no back arrow.&lt;/li&gt;
&lt;li&gt;I never cared for the swipe left and right to switch articles/ads, swipe up and down for more pages of content. I think it works okay for scrolling apps; &lt;a href="http://www.phantomfish.com/byline.html"&gt;Byline&lt;/a&gt; works that way, for example. But not so much for pagination. No other reading apps work like that. And since some of the chevron controls also scroll left and right, they seem to behave differently but show the same animation.&lt;/li&gt;
&lt;li&gt;Some pages looks as though you ought to be able to tap something, such as an invitation to watch a video or a description of an article (especially in the "In This Issue" section), but nothing happens when you tap. They really ought to respond to taps.&lt;/li&gt;
&lt;li&gt;Other places are less obvious that should be tapped for more info, but there are instructions, such as a little circle in the Special Edition that says "tap circle to show caption." I tap the circle and the caption appears, right where the tip had been. Why not just show the caption?&lt;/li&gt;
&lt;li&gt;Some articles have sharing features, where you can share via Twitter, Facebook, or email. The Twitter feature connects you to twitter.com in a web view, rather than use the iOS 5 Twitter support. Worse, it does not remember that I logged in between sessions. So if I tweeted yesterday and want to do it again today, I have to log in again. I suspect the Facebook feature works the same. Sharing via Email just opens the Mail app, rather than use the embedded iOS email controller.&lt;/li&gt;
&lt;li&gt;When I use the table of contents popover, it is always scrolled to the top, no matter how far down the currently-displayed article is or where I left it the last time I used it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are other issues, as well, some minor, such as the boring grey background if you make an image or article bounce when you scroll past the end, or the display of the issue name in the iTunes store as "Vogue Magazine_200_30." Other issues are more annoying, such as the difficulty of discovering and managing the different types and layers of navigation. But honestly, if just the following issues were addressed, the app would be &lt;em&gt;so&lt;/em&gt; much better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use plain text for layout. HTML would be great.&lt;/li&gt;
&lt;li&gt;Get the download size down. Reducing the use of images for laying out text will help a lot for &lt;em&gt;The New Yorker,&lt;/em&gt; I'm sure; less perhaps for &lt;em&gt;Vogue&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Allow the downloads to happen in the background, no matter what else I'm doing.&lt;/li&gt;
&lt;li&gt;Make less use of embedded web browsers for stuff, or at least cache them. This is a tablet, with a lot of great features built in. Take advantage of them to make the app as responsive as possible!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These steps will help a lot. But even then, I can't help but think that there continues to be more need for UX exploration and experimentation. A tablet is &lt;em&gt;not&lt;/em&gt; a magazine and &lt;em&gt;not&lt;/em&gt; a web browser, but offers its own features and constraints. I think a better fit for selling editorial or image-based content could still be created; these apps don't come close. I can think of three reasons for why not:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The limitations of the Adobe publishing platform. It's a lowest-common denominator experience, in that the player has to work on a bunch of different devices, and so would suck on all of them. And it just might not have the controls for a strong text-based layout, though I don't see why Adobe wouldn't have the resources to address that issue.&lt;/li&gt;
&lt;li&gt;The desire for layout integrity. But as &lt;a href="http://reverttosaved.com/2012/03/26/why-do-magazines-look-so-bad-on-the-new-ipad/"&gt;Craig Grannell writes&lt;/a&gt;, that's akin to 90s web sites that were nothing but a single big image. It didn't work well for a whole slew of reasons.&lt;/li&gt;
&lt;li&gt;Copyright. Plain-text content would just be too easy to "steal," but if it's in a PNG, no one will bother. I know nothing about this personally, but it would not surprise me if there were folks inside Condé and some of these other magazines who don't want to use text-based content because it would be too easy to copy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I find none of these reasons compelling. &lt;/p&gt;

&lt;p&gt;Look, I write this out of love. &lt;em&gt;The New Yorker&lt;/em&gt; is my favorite magazine, bar none, and I want it to succeed. Print is dying, but there is &lt;em&gt;so&lt;/em&gt; much opportunity on devices like the iPad. Continue to create the best content, and provide it in a form factor and experience that takes advantage of the features and limitations of your targeted platforms, to allow readers to &lt;em&gt;enjoy&lt;/em&gt; reading, and success will be assured.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/XkBOHIOwImA" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://justatheory.com/computers/apps/conde-nast-ipad.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/databases/sql-change-management-sans-redundancy</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/TloDXCSP52M/sql-change-management-sans-redundancy.html" />
<title type="text/plain">SQL Change Management Sans Duplication</title>
<dc:subject>sql</dc:subject>
<dc:subject>database</dc:subject>
<dc:subject>change+management</dc:subject>
<dc:subject>deploy</dc:subject>
<dc:subject>revert</dc:subject>
<dc:subject>vcs</dc:subject>
<dc:subject>git</dc:subject>
<dc:subject>duplication</dc:subject>
<issued>2012-01-30T16:00:00Z</issued>
<modified>2012-01-30T16:00:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="text/html" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="escaped">
&lt;p&gt;In the &lt;a href="/computers/databases/vcs-sql-change-management.html"&gt;previous episode&lt;/a&gt;
in this series, I had one more issue with regard to SQL change management that
I wanted to resolve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There is still more duplication of code than I would like, in that a
procedure defined in one change script would have to be copied whole
to a new script for any changes, even simple single-line changes.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;So let&amp;rsquo;s see what we can do about that. Loading it into Git, our
&lt;a href="/computers/databases/simple-sql-change-management.html"&gt;first example&lt;/a&gt; looks
like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; alias sqlhist="git log -p --format='[%H%d]' --name-only --reverse sql/deploy \
| awk '/^\[/ {print \"\"} /./'"
&amp;gt; sqlhist

[3852b378aa029cc610a03806e8268ed452dce8a6 (alpha)]
sql/deploy/users_table.sql

[32883d5a08691351b07928fa4e4fb7e68c500973 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[b8b9f5c152675305c6b2d3e105d55a25019e0828 (HEAD, gamma, master)]
sql/deploy/add_user.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(Aside: I&amp;rsquo;ve created an alias, &lt;code&gt;sqlhist&lt;/code&gt;, on the first line, so that all the Git
and Awk magic doesn&amp;rsquo;t clutter the remaining examples.)&lt;/p&gt;

&lt;p&gt;So, we&amp;rsquo;ve got the creation of the &lt;code&gt;users&lt;/code&gt; table under the &lt;code&gt;alpha&lt;/code&gt; tag, the
addition of the &lt;code&gt;widgets&lt;/code&gt; table and an accompanying &lt;code&gt;add_widget()&lt;/code&gt; function
under the &lt;code&gt;beta&lt;/code&gt; tag, and the creation of the &lt;code&gt;add_user()&lt;/code&gt; function under the
&lt;code&gt;gamma&lt;/code&gt; tag. So far so good. Now, let&amp;rsquo;s say that &lt;code&gt;gamma&lt;/code&gt; has been deployed to
production, and now we&amp;rsquo;re ready to add a feature for the next release.&lt;/p&gt;

&lt;h3&gt;Modify This&lt;/h3&gt;

&lt;p&gt;It turns out that our users really want a timestamp for the time a widget was
created. So let&amp;rsquo;s add a new change script that adds a &lt;code&gt;created_at&lt;/code&gt; column to
the &lt;code&gt;widgets&lt;/code&gt; table. First we add &lt;code&gt;sql/deploy/widgets_created_at.sql&lt;/code&gt; with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- requires: widgets_table
ALTER TABLE widgets ADD created_at TIMESTAMPTZ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And then the accompanying revert script, &lt;code&gt;sql/revert/widgets_created_at.sql&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ALTER TABLE widgets DROP COLUMN IF EXISTS created_at;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Commit them and now our deployment configuration looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; sqlhist

[3852b378aa029cc610a03806e8268ed452dce8a6 (alpha)]
sql/deploy/users_table.sql

[32883d5a08691351b07928fa4e4fb7e68c500973 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[b8b9f5c152675305c6b2d3e105d55a25019e0828 (gamma)]
sql/deploy/add_user.sql

[44ba615b7813531f0acb6810cbf679791fe57bf2 (HEAD, master)]
sql/deploy/widgets_created_at.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So far so good. We have a simple delta script that modifies the existing
table, and there is no code duplication. Time to modify the &lt;code&gt;add_widget()&lt;/code&gt;
function to insert the timestamp. Recall that, in the
&lt;a href="/computers/databases/simple-sql-change-management.html"&gt;first article&lt;/a&gt; in
this series, I created a separate &lt;code&gt;sql/deploy/add_widgets_v2.sql&lt;/code&gt; file, copied
the existing function in its entirety into the new file, and modified it
there. If we were to do that here, the resulting deployment configuration
would look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; sqlhist

[3852b378aa029cc610a03806e8268ed452dce8a6 (alpha)]
sql/deploy/users_table.sql

[32883d5a08691351b07928fa4e4fb7e68c500973 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[b8b9f5c152675305c6b2d3e105d55a25019e0828 (gamma)]
sql/deploy/add_user.sql

[44ba615b7813531f0acb6810cbf679791fe57bf2]
sql/deploy/widgets_created_at.sql

[dfba488cfd9145928a25d8d48de3231da84s4bd2 (HEAD, master)]
sql/deploy/add_widget_v2.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which would be fine, except that if someone else wanted to see what had
changed, here&amp;rsquo;s what &lt;code&gt;git diff&lt;/code&gt; would output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git diff HEAD^ sql/deploy 
diff --git a/sql/deploy/add_widget_v2.sql b/sql/deploy/add_widget_v2.sql
new file mode 100644
index 0000000..9132195
--- /dev/null
+++ b/sql/deploy/add_widget_v2.sql
@@ -0,0 +1,8 @@
+-- requires widgets_created_at
+CREATE OR REPLACE FUNCTION add_widget(
+    username   TEXT,
+    widgetname TEXT
+) RETURNS VOID LANGUAGE SQL AS $$
+    INSERT INTO widgets (created_by, name, created_at)
+    VALUES ($1, $2, NOW());
+$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So, what changed in the &lt;code&gt;add_widget()&lt;/code&gt; function between &lt;code&gt;gamma&lt;/code&gt; and now? One
cannot tell from this diff: it looks like a brand new function. And no
web-based VCS interface will show you, either; it&amp;rsquo;s just not inherent in the
commit. We have to actually &lt;em&gt;know&lt;/em&gt; that it was just an update to an existing
function, and what files to manually diff, like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt; &amp;gt; diff -u sql/deploy/add_widget.sql sql/deploy/add_widget_v2.sql 
--- sql/deploy/add_widget.sql   2012-01-28 13:06:24.000000000 -0800
+++ sql/deploy/add_widget_v2.sql    2012-01-28 13:26:59.000000000 -0800
@@ -1,8 +1,8 @@
--- requires: widgets_table
-
+-- requires: widgets_created_at
 CREATE OR REPLACE FUNCTION add_widget(
     username   TEXT,
     widgetname TEXT
 ) RETURNS VOID LANGUAGE SQL AS $$
-    INSERT INTO widgets (created_by, name) VALUES ($1, $2);
+    INSERT INTO widgets (created_by, name, created_at)
+    VALUES ($1, $2, NOW());
 $$;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Much better, but how annoying is that? It doesn&amp;rsquo;t allow us to really take
advantage of the VCS, all because we need SQL changes to run in a very
specific order.&lt;/p&gt;

&lt;p&gt;But let&amp;rsquo;s ignore that for the moment. Let&amp;rsquo;s just throw out the commit with
&lt;code&gt;add_widgets_v2.sql&lt;/code&gt; and go ahead and change the &lt;code&gt;add_widget&lt;/code&gt; change script
directly. So the history now looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; sqlhist

[3852b378aa029cc610a03806e8268ed452dce8a6 (alpha)]
sql/deploy/users_table.sql

[32883d5a08691351b07928fa4e4fb7e68c500973 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[b8b9f5c152675305c6b2d3e105d55a25019e0828 (gamma)]
sql/deploy/add_user.sql

[44ba615b7813531f0acb6810cbf679791fe57bf2]
sql/deploy/widgets_created_at.sql

[e4b970aa36f27451fe377791eab040a73c6eb47a (HEAD, epsilon, master)]
sql/deploy/add_widget.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Naturally, the &lt;code&gt;add_widget&lt;/code&gt; script appears twice now, once under the &lt;code&gt;beta&lt;/code&gt;
tag and once under &lt;code&gt;epsilon&lt;/code&gt; (which I&amp;rsquo;ve just tagged). What are the
consequences for our migration? Well, if we were to build a new database from
the beginning, running these migrations as listed here, we would get an error
while applying the &lt;code&gt;beta&lt;/code&gt; changes:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ERROR:  column "created_at" of relation "widgets" does not exist
LINE 5:     INSERT INTO widgets (created_by, name, created_at)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is because the &lt;code&gt;created_at&lt;/code&gt; column won&amp;rsquo;t exist until the
&lt;code&gt;widgets_created_at&lt;/code&gt; change is applied. That won&amp;rsquo;t do, will it? Fortunately,
Git knows exactly what the &lt;code&gt;add_widget&lt;/code&gt; deploy script looked like under the
&lt;code&gt;beta&lt;/code&gt; tag, and we can ask it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git show beta:sql/deploy/add_widget.sql
-- requires: widgets_table

CREATE OR REPLACE FUNCTION add_widget(
    username   TEXT,
    widgetname TEXT
) RETURNS VOID LANGUAGE SQL AS $$
    INSERT INTO widgets (created_by, name) VALUES ($1, $2);
$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Boom, there it is, with no reference to &lt;code&gt;created_at&lt;/code&gt;. Using this technique,
our SQL deployment app can successfully apply all of our database changes by
iterating over the list of changes and applying the contents of each script
&lt;em&gt;at the time of the appropriate commit or tag.&lt;/em&gt; In other words, it could apply
the output from each of these commands:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git show alpha:sql/deploy/users_table.sql
git show beta:sql/deploy/widgets_table.sql
git show beta:sql/deploy/add_widget.sql
git show gamma:sql/deploy/add_user.sql
git show 44ba615b7813531f0acb6810cbf679791fe57bf2:sql/deploy/widget_created_at.sql
git show epsilon:sql/deploy/add_widget.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And everything will work exactly as it should: the original version of the
&lt;code&gt;add_widget&lt;/code&gt; change script will be for the &lt;code&gt;beta&lt;/code&gt; tag, and the next version
will be applied for the &lt;code&gt;epsilon&lt;/code&gt; tag. Not bad, right? We get a nice, clean
Git history &lt;em&gt;and&lt;/em&gt; can exploit it to manage the changes.&lt;/p&gt;

&lt;h3&gt;Reversion to the Mean&lt;/h3&gt;

&lt;p&gt;But what about reversion? What if the deploy to &lt;code&gt;epsilon&lt;/code&gt; failed, and we need
to revert back to &lt;code&gt;gamma&lt;/code&gt;? Recall that in the
&lt;a href="/computers/databases/simple-sql-change-management.html"&gt;first article&lt;/a&gt;, I
eliminated duplication by having the &lt;code&gt;add_widget_v2&lt;/code&gt; revert script simply
call the &lt;code&gt;add_widget&lt;/code&gt; deploy script. But such is not possible now that we&amp;rsquo;ve
changed &lt;code&gt;add_widget&lt;/code&gt; in place. What to do?&lt;/p&gt;

&lt;p&gt;The key is for the change management script to know the difference between a
new change script and a modified one. Fortunately, Git knows that, too, and we
can get it to cough up that information with a simple change to the &lt;code&gt;sqlhist&lt;/code&gt;
alias: instead of passing &lt;code&gt;--name-only&lt;/code&gt;, pass &lt;code&gt;--name-status&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;% alias sqlhist="git log -p --format='[%H%d]' --name-status --reverse sql/deploy \
| awk '/^\[/ {print \"\"} /./'"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Using this new alias, our history looks like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; sqlhist

[3852b378aa029cc610a03806e8268ed452dce8a6 (alpha)]
A   sql/deploy/users_table.sql

[32883d5a08691351b07928fa4e4fb7e68c500973 (beta)]
A   sql/deploy/add_widget.sql
A   sql/deploy/widgets_table.sql

[b8b9f5c152675305c6b2d3e105d55a25019e0828 (gamma)]
A   sql/deploy/add_user.sql

[44ba615b7813531f0acb6810cbf679791fe57bf2]
A   sql/deploy/widgets_created_at.sql

[e4b970aa36f27451fe377791eab040a73c6eb47a (HEAD, epsilon, master)]
M   sql/deploy/add_widget.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we have a letter defining the status of each file. An &amp;ldquo;A&amp;rdquo; means the file
was added in that commit; an &amp;ldquo;M&amp;rdquo; means it was modified. But the upshot is
that, to revert to &lt;code&gt;gamma&lt;/code&gt;, our change management can see that &lt;code&gt;add_widget&lt;/code&gt;
was modified in &lt;code&gt;epsilon&lt;/code&gt;, and, rather than apply a revert change script, it
can just apply the version of the script as it existed under &lt;code&gt;gamma&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git show gamma:sql/deploy/add_widget.sql
-- requires: widgets_table

CREATE OR REPLACE FUNCTION add_widget(
    username   TEXT,
    widgetname TEXT
) RETURNS VOID LANGUAGE SQL AS $$
    INSERT INTO widgets (created_by, name) VALUES ($1, $2);
$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And there we are, right back to where we should be. Of course, the remaining
&lt;code&gt;epsilon&lt;/code&gt; deploy script, &lt;code&gt;widget_created_at&lt;/code&gt;, was added in its commit, so we
just apply the revert script and we&amp;rsquo;re set, back to &lt;code&gt;gamma&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;Still Configurable&lt;/h3&gt;

&lt;p&gt;To get back to the original idea of a migration configuration file, I still
think it&amp;rsquo;s entirely do-able. All we need to is to have the change management
app generate it, just &lt;a href="/databases/vcs-sql-change-management.html"&gt;as before&lt;/a&gt;.
When it comes to modified &amp;mdash; rather than added &amp;mdash; deploy scripts, it can
automatically insert new scripts with the full copies of previous versions,
much as before. The resulting configuration would look something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[3852b378aa029cc610a03806e8268ed452dce8a6 (alpha)]
sql/deploy/users_table.sql

[32883d5a08691351b07928fa4e4fb7e68c500973 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[b8b9f5c152675305c6b2d3e105d55a25019e0828 (gamma)]
sql/deploy/add_user.sql

[44ba615b7813531f0acb6810cbf679791fe57bf2]
sql/deploy/widgets_created_at.sql

[e4b970aa36f27451fe377791eab040a73c6eb47a (HEAD, epsilon, master)]
sql/deploy/add_widget_v2.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that last line, where we now have &lt;code&gt;add_widget_v2&lt;/code&gt;. The change management
script would simply generate this file, and create an additional revert script
with the same name that just contains the contents of the deploy script as it
was under the &lt;code&gt;gamma&lt;/code&gt; tag.&lt;/p&gt;

&lt;h3&gt;Too Baroque?&lt;/h3&gt;

&lt;p&gt;Having written down these ideas that have plagued by brain for the last week,
along with some examples using Git to confirm them, I&amp;rsquo;m convinced more than
ever that this is entirely workable. But it also leads me to wonder if it&amp;rsquo;s
too baroque. I intend these posts as a rough spec for how this thing should
work, and I plan to implement it in the coming weeks. But I&amp;rsquo;m wondering how
difficult it will be to explain it all to people?&lt;/p&gt;

&lt;p&gt;So let me see if I can break it down to a few simple rules.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In general, you should create independent deploy and revert scripts for your
SQL. Put a &lt;code&gt;CREATE TABLE&lt;/code&gt; statement into its own script. If it requires some
some other table, require declare the dependency. If you need to change it
later, create a new script that uses &lt;code&gt;ALTER TABLE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In special cases where a simple change cannot be made without copying
something wholesale, and where the deploy script is idempotent, you may
simply modify the deploy script in-place.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;That&amp;rsquo;s about it. The &lt;a href="http://en.wikipedia.org/wiki/Idempotence"&gt;idempotence&lt;/a&gt;
of the deploy script is important for ensuring consistency, and applies very
well to features such as
&lt;a href="http://www.postgresql.org/docs/current/static/xfunc.html"&gt;user-defined functions&lt;/a&gt;.
For other objects, there are generally &lt;code&gt;ALTER&lt;/code&gt; statements that allow changes
to be made without wholesale copying of existing code.&lt;/p&gt;

&lt;p&gt;So what am I missing? What have I overlooked? What mistakes in my logic have I
made? Do you think this will be too tricky to implement, or to use? Is it hard
to understand? Your comments would be greatly appreciated, because I &lt;em&gt;am&lt;/em&gt;
going to write an app to do this stuff, and want to get it &lt;em&gt;right&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for sticking with me through all the thought experiments. For my next
post on this topic, I expect to have an interface spec for the new app.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/TloDXCSP52M" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://justatheory.com/computers/databases/sql-change-management-sans-redundancy.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/databases/vcs-sql-change-management</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/QTYAuGwQ6yE/vcs-sql-change-management.html" />
<title type="text/plain">VCS-Enabled SQL Change Management</title>
<dc:subject>sql</dc:subject>
<dc:subject>database</dc:subject>
<dc:subject>change+management</dc:subject>
<dc:subject>deploy</dc:subject>
<dc:subject>revert</dc:subject>
<dc:subject>vcs</dc:subject>
<dc:subject>git</dc:subject>
<issued>2012-01-27T07:32:00Z</issued>
<modified>2012-01-27T07:32:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="text/html" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="escaped">
&lt;p&gt;In my &lt;a href="/computers/databases/simple-sql-change-management.html"&gt;previous post&lt;/a&gt;,
I outlined the basics of a configuration-file and dependency-tracking SQL
deployment architecture, but left a couple of additional challenges
unresolved. They were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;I would rather not have to hand-edit a configuration file, as it it&amp;rsquo;s
finicky and error-prone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is still more duplication of code than I would like, in that a
procedure defined in one change script would have to be copied whole
to a new script for any changes, even single-line simple changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;I believe I can solve both of these issues by simple use of a VCS. Since all
of my current projects currently use Git, I will use it for the examples here.&lt;/p&gt;

&lt;h3&gt;Git it On&lt;/h3&gt;

&lt;p&gt;First, recall the structure of the configuration file, which was something
like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[alpha]
users_table

[beta]
add_widget
widgets_table

[gamma]
add_user

[delta]
widgets_created_at
add_widget_v2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Basically, we have bracketed tags identifying changes that should be deployed.
Now have a look at this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git log -p --format='[%H]' --name-only --reverse sql/deploy
[8920aaf7947a56f6777e69a21b70fd877c8fd6dc]

sql/deploy/users_table.sql
[f7da5fd4b7391747f75d85db6fa82de47b9e4c00]

sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql
[ea10b9e566934ef256debe8752504189436e162a]

sql/deploy/add_user.sql
[89e85f98d891a2984ad4e3c42d8ca8cf31f3b2b4]

sql/deploy/add_widget_v2.sql
sql/deploy/widgets_created_at.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Look familiar? Let&amp;rsquo;s use a bit of &lt;code&gt;awk&lt;/code&gt; magic to neaten things a bit (Thanks
&lt;a href="http://technosorcery.net/"&gt;helwig&lt;/a&gt;!):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git log -p --format='[%H]' --name-only --reverse sql/deploy \
| awk '/^\[/ {print ""} /./'

[8920aaf7947a56f6777e69a21b70fd877c8fd6dc]
sql/deploy/users_table.sql

[f7da5fd4b7391747f75d85db6fa82de47b9e4c00]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[ea10b9e566934ef256debe8752504189436e162a]
sql/deploy/add_user.sql

[89e85f98d891a2984ad4e3c42d8ca8cf31f3b2b4]
sql/deploy/add_widget_v2.sql
sql/deploy/widgets_created_at.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ah, that&amp;rsquo;s better. We have commit SHA1s for tags, followed by the appropriate
lists of deployment scripts. But wait, we can decorate it, too:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git log -p --format='[%H%d]' --name-only --reverse sql/deploy \
| awk '/^\[/ {print ""} /./'

[8920aaf7947a56f6777e69a21b70fd877c8fd6dc (alpha)]
sql/deploy/users_table.sql

[f7da5fd4b7391747f75d85db6fa82de47b9e4c00 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[ea10b9e566934ef256debe8752504189436e162a (gamma)]
sql/deploy/add_user.sql
[89e85f98d891a2984ad4e3c42d8ca8cf31f3b2b4 (HEAD, delta, master)]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Look at that! Actual VCS tags built right in to the output. So, assuming our
deployment app can parse this output, we can deploy or revert to any commit or
tag. Better yet, we don&amp;rsquo;t have to maintain a configuration file, because the
VCS is already tracking all that stuff for us! Our change management app can
automatically detect if we&amp;rsquo;re in a Git repository (or Mercurial or CVS or
Subversion or whatever) and fetch the necessary information for us. It&amp;rsquo;s all
there in the history. We can name revision identifiers (SHA1s here) to deploy
or revert to, or use tags (alpha, beta, gamma, delta, HEAD, or master in this
example).&lt;/p&gt;

&lt;p&gt;And with careful repository maintenance, this approach will work for branches,
as well. For example, say you have developers working in two branches,
&lt;code&gt;feature_foo&lt;/code&gt; and &lt;code&gt;feature_bar&lt;/code&gt;. In &lt;code&gt;feature_foo&lt;/code&gt;, a &lt;code&gt;foo_table&lt;/code&gt; change script
gets added in one commit, and an &lt;code&gt;add_foo&lt;/code&gt; script in a second commit. Merge it
into master and the history now looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git log -p --format='[%H%d]' --name-only --reverse sql/deploy \
| awk '/^\[/ {print ""} /./'

[8920aaf7947a56f6777e69a21b70fd877c8fd6dc (alpha)]
sql/deploy/users_table.sql

[f7da5fd4b7391747f75d85db6fa82de47b9e4c00 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[ea10b9e566934ef256debe8752504189436e162a (gamma)]
sql/deploy/add_user.sql

[89e85f98d891a2984ad4e3c42d8ca8cf31f3b2b4 (delta)]
sql/deploy/add_widget_v2.sql
sql/deploy/widgets_created_at.sql

[cbb48144065dd345c5248e5f1e42c1c7391a88ed]
sql/deploy/foo_table.sql

[7f89e23c9f1e7fc298c69400f6869d701f76759e (HEAD, master, feature_foo)]
sql/deploy/add_foo.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So far so good.&lt;/p&gt;

&lt;p&gt;Meanwhile, development in the &lt;code&gt;feature_bar&lt;/code&gt; branch has added a &lt;code&gt;bar_table&lt;/code&gt;
change script in one commit and &lt;code&gt;add_bar&lt;/code&gt; in another. Because development in
this branch was going on concurrently with the &lt;code&gt;feature_foo&lt;/code&gt; branch, if we
just merged it into master, we might get a history like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git log -p --format='[%H%d]' --name-only --reverse sql/deploy \
| awk '/^\[/ {print ""} /./'
[8920aaf7947a56f6777e69a21b70fd877c8fd6dc (alpha)]
sql/deploy/users_table.sql

[f7da5fd4b7391747f75d85db6fa82de47b9e4c00 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[ea10b9e566934ef256debe8752504189436e162a (gamma)]
sql/deploy/add_user.sql

[89e85f98d891a2984ad4e3c42d8ca8cf31f3b2b4 (delta)]
sql/deploy/add_widget_v2.sql
sql/deploy/widgets_created_at.sql

[cbb48144065dd345c5248e5f1e42c1c7391a88ed]
sql/deploy/foo_table.sql

[d1882d7b4cfcf5c57030bd5a15f8571bfd7e48e2]
sql/deploy/bar_table.sql

[7f89e23c9f1e7fc298c69400f6869d701f76759e]
sql/deploy/add_foo.sql

[2330da1caae9a46ea84502bd028ead399ca3ca02 (feature_bar)]
sql/deploy/add_bar.sql

[73979ede2c8589cfe24c9213a9538f305e6f508f (HEAD, master, feature_foo)]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note that &lt;code&gt;bar_table&lt;/code&gt; comes before &lt;code&gt;add_foo&lt;/code&gt;. In other words, the
&lt;code&gt;feature_foo&lt;/code&gt; and &lt;code&gt;feature_bar&lt;/code&gt; commits are interleaved. If we were to deploy
to &lt;code&gt;HEAD&lt;/code&gt;, and then need to revert &lt;code&gt;feature_bar&lt;/code&gt;, &lt;code&gt;bar_table&lt;/code&gt; would not be
reverted. This is, shall we say, less than desirable.&lt;/p&gt;

&lt;p&gt;There are at least two ways to avoid this issue. One is to squash the merge
into a single commit using &lt;code&gt;git merge --squash feature_bar&lt;/code&gt;. This would be
similar to accepting a single patch and applying it. The resulting history
would look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git log -p --format='[%H%d]' --name-only --reverse sql/deploy \
| awk '/^\[/ {print ""} /./'

[8920aaf7947a56f6777e69a21b70fd877c8fd6dc (alpha)]
sql/deploy/users_table.sql

[f7da5fd4b7391747f75d85db6fa82de47b9e4c00 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[ea10b9e566934ef256debe8752504189436e162a (gamma)]
sql/deploy/add_user.sql

[89e85f98d891a2984ad4e3c42d8ca8cf31f3b2b4 (delta)]
sql/deploy/add_widget_v2.sql
sql/deploy/widgets_created_at.sql

[cbb48144065dd345c5248e5f1e42c1c7391a88ed]
sql/deploy/foo_table.sql

[7f89e23c9f1e7fc298c69400f6869d701f76759e]
sql/deploy/add_foo.sql

[91a048c05e0444682e2e4763e8a7999a869b4a77 (HEAD, master)]
sql/deploy/add_bar.sql
sql/deploy/bar_table.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now both of the &lt;code&gt;feature_bar&lt;/code&gt; change scripts come after the &lt;code&gt;feature_foo&lt;/code&gt;
changes. But it might be nice to keep the history. So a better solution (and
the best practice, I believe), is to rebase the &lt;code&gt;feature_bar&lt;/code&gt; branch before
merging it into master, like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git rebase master
First, rewinding head to replay your work on top of it...
Applying: Add bar.
Applying: Add add_bar().
&amp;gt; git checkout master
Switched to branch 'master'
&amp;gt; git merge feature_bar
Updating 7f89e23..0fab7a0
Fast-forward
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 sql/deploy/add_bar.sql
 create mode 100644 sql/deploy/bar_table.sql
 create mode 100644 sql/revert/add_bar.sql
 create mode 100644 sql/revert/bar_table.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And now we should have:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git log -p --format='[%H%d]' --name-only --reverse sql/deploy \
| awk '/^\[/ {print ""} /./'

[8920aaf7947a56f6777e69a21b70fd877c8fd6dc (alpha)]
sql/deploy/users_table.sql

[f7da5fd4b7391747f75d85db6fa82de47b9e4c00 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[ea10b9e566934ef256debe8752504189436e162a (gamma)]
sql/deploy/add_user.sql

[89e85f98d891a2984ad4e3c42d8ca8cf31f3b2b4 (delta)]
sql/deploy/add_widget_v2.sql
sql/deploy/widgets_created_at.sql

[cbb48144065dd345c5248e5f1e42c1c7391a88ed]
sql/deploy/foo_table.sql

[7f89e23c9f1e7fc298c69400f6869d701f76759e]
sql/deploy/add_foo.sql

[0e53c29eb47c618d0a8818cc17bd5a0aab0acd6d]
sql/deploy/bar_table.sql

[0fab7a0ba928b34a46a9495d4efc1c73d9133d37 (HEAD, master, feature_bar)]
sql/deploy/add_bar.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Awesome, now everything is in the correct order. We did lose the &lt;code&gt;feature_foo&lt;/code&gt;
&amp;ldquo;tag,&amp;rdquo; though. That&amp;rsquo;s because it wasn&amp;rsquo;t a tag, and neither is &lt;code&gt;feature_bar&lt;/code&gt;
here. They are, rather, branch names, which we becomes obvious when using
&amp;ldquo;full&amp;rdquo; decoration:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git log --format='%d' --decorate=full HEAD^..      
 (HEAD, refs/heads/master, refs/heads/feature_foo)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After the next commit, it will disappear from the history. So let&amp;rsquo;s just tag the
relevant commits ourselves:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;gt; git tag feature_foo 7f89e23c9f1e7fc298c69400f6869d701f76759e
&amp;gt; git tag feature_bar
&amp;gt; git log -p --format='[%H%d]' --name-only --reverse sql/deploy \
| awk '/^\[/ {print ""} /./'

[8920aaf7947a56f6777e69a21b70fd877c8fd6dc (alpha)]
sql/deploy/users_table.sql

[f7da5fd4b7391747f75d85db6fa82de47b9e4c00 (beta)]
sql/deploy/add_widget.sql
sql/deploy/widgets_table.sql

[ea10b9e566934ef256debe8752504189436e162a (gamma)]
sql/deploy/add_user.sql

[89e85f98d891a2984ad4e3c42d8ca8cf31f3b2b4 (delta)]
sql/deploy/add_widget_v2.sql
sql/deploy/widgets_created_at.sql

[cbb48144065dd345c5248e5f1e42c1c7391a88ed]
sql/deploy/foo_table.sql

[7f89e23c9f1e7fc298c69400f6869d701f76759e (feature_foo)]
sql/deploy/add_foo.sql

[0e53c29eb47c618d0a8818cc17bd5a0aab0acd6d]
sql/deploy/bar_table.sql

[0fab7a0ba928b34a46a9495d4efc1c73d9133d37 (HEAD, feature_bar, master, feature_bar)]
sql/deploy/add_bar.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ah, there we go! After the next commit, one of those &lt;code&gt;feature_bar&lt;/code&gt;s will
disappear, since the branch will have been left behind. But we&amp;rsquo;ll still have
the tag.&lt;/p&gt;

&lt;h3&gt;Not Dead Yet&lt;/h3&gt;

&lt;p&gt;Clearly we can intelligently use Git to manage SQL change management. (Kind of
stands to reason, doesn&amp;rsquo;t it?) Nevertheless, I believe that a configuration
file still might have its uses. Not only because not every project is in a VCS
(it ought to be!), but because oftentimes a project is not deployed to
production as a git clone. It might be distributed as a source tarball or an
RPM. In such a case, including a configuration file in the distribution would
be very useful. But there is still no need to manage it by hand; our
deployment app can generate it from the VCS history before packaging for
release.&lt;/p&gt;

&lt;h3&gt;More to Come&lt;/h3&gt;

&lt;p&gt;I&amp;rsquo;d planned to cover the elimination of duplication, but I think this is
enough for one post. Watch for that idea in my next post.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/QTYAuGwQ6yE" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://justatheory.com/computers/databases/vcs-sql-change-management.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2012:/computers/databases/simple-sql-change-management</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/LytHP6uuIHk/simple-sql-change-management.html" />
<title type="text/plain">Simple SQL Change Management</title>
<dc:subject>sql</dc:subject>
<dc:subject>database</dc:subject>
<dc:subject>change+management</dc:subject>
<dc:subject>versioning</dc:subject>
<dc:subject>deploy</dc:subject>
<dc:subject>revert</dc:subject>
<issued>2012-01-26T05:00:00Z</issued>
<modified>2012-01-26T05:00:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="text/html" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="escaped">
&lt;p&gt;I&amp;rsquo;ve been thinking a lot about SQL change management. I know I have
&lt;a href="/computers/databases/change-management.html"&gt;written about this before&lt;/a&gt;. But
I was never satisfied with that idea, mostly because it required managing
database changes in two separate but interdependent ways. Blargh. So for my
Perl projects the last couple of years, I have stuck to the very simple but
ugly Rails-style migration model, as implemented in
&lt;a href="https://metacpan.org/module/Module::Build::DB"&gt;Module::Build::DB&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But it has been on my brain more lately because I&amp;rsquo;m writing more and more
database applications &lt;a href="http://iovation.com/"&gt;at work&lt;/a&gt;, and managing changes
over time is becoming increasingly annoying. I&amp;rsquo;ve been using a variation on
&lt;a href="http://www.depesz.com/index.php/2010/08/22/versioning/"&gt;Depesz&amp;rsquo;s Versioning&lt;/a&gt;
package, mainly because its idea of specifying dependencies instead of ordered
deployment scripts is so useful. However, its implementation in pure SQL, with
accompanying shell and Perl scripts, is not entirely satisfying. Worse, one
cannot easily include the contents of an earlier deployment script in a
reversion script, because the dependency registration function embedded in a
script will throw an error if it has been run before. The upshot is that if
you make a one-line change to a database function, you still have to paste the
entire thing into a new file and commit it to your source code repository.
This makes tracking diffs annoying.&lt;/p&gt;

&lt;p&gt;Oh, and did I mention that there is no simple built-in way to revert changes,
and even if there were, because there are no named releases, it can be
difficult to decide what to revert &lt;em&gt;to&lt;/em&gt;? I don&amp;rsquo;t often need that capability,
but when I need it, I &lt;strong&gt;need it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then, this week, Robert Haas
&lt;a href="http://archives.postgresql.org/pgsql-hackers/2012-01/msg01138.php"&gt;described a deployment implementation&lt;/a&gt;
he implemented. It was simple:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;My last implementation worked by keeping a schema_versions table on the
server with one column, a UUID. The deployment tarball contained a file with
a list of UUIDs in it, each one associated to an SQL script. At install
time, the install script ran through that file in order and ran any scripts
whose UUID didn&amp;rsquo;t yet appear in the table, and then added the UUIDs of the
run scripts to the table.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I like this simplicity, but there are some more things I think could be done,
including dependency reslolution and reversion. And it seems silly to have a
UUID stand for a script name; why not just list script names? Better yet, tag
groups of changes for easy reference.&lt;/p&gt;

&lt;h3&gt;Yet Another SQL Deployment Strategy&lt;/h3&gt;

&lt;p&gt;So here&amp;rsquo;s my proposal. Following Robert, we create a configuration file, but
instead of just listing changes, we fill it with tags and the names of the
changes are associated with each. An example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[alpha]
users_table

[beta]
add_widget
widgets_table

[gamma]
add_user
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Our change management app will parse this file, finding the tag for each stage
of the migration in brackets, and apply the associated changes, simply finding
each of them in &lt;code&gt;sql/deploy/$change.sql&lt;/code&gt;. If it&amp;rsquo;s reverting changes, it finds
the reversion scripts named &lt;code&gt;sql/revert/$change.sql&lt;/code&gt;. The tags can be anything
you want; release tags might be useful. Easy so far, right?&lt;/p&gt;

&lt;p&gt;Except notice that I have a minor ordering problem here. The &lt;code&gt;add_widget&lt;/code&gt;
change, which adds a function to insert a record into the &lt;code&gt;widgets&lt;/code&gt; table,
comes &lt;em&gt;before&lt;/em&gt; the &lt;code&gt;widgets_table&lt;/code&gt; script. If we run the &lt;code&gt;add_widget&lt;/code&gt; change
first, it will fail, because the &lt;code&gt;widgets&lt;/code&gt; table does not yet exist.&lt;/p&gt;

&lt;p&gt;Of course we can re-order the lines in the configuration file. But given that
one might have many changes for a particular tag, with many cross-referenceing
dependencies, I think it&amp;rsquo;s better to overcome this problem in the scripts
themselves. So I suggest that the &lt;code&gt;sql/deploy/add_widget.sql&lt;/code&gt; file look
something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- requires: widgets_table

CREATE OR REPLACE FUNCTION add_widget(
    username   TEXT,
    widgetname TEXT
) RETURNS VOID LANGUAGE SQL AS $$
    INSERT INTO widgets (created_by, name) VALUES ($1, $2);
$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here I&amp;rsquo;m stealing Depesz&amp;rsquo;s dependency tracking idea. With a simple comment at
the top of the script, we specify that this change requires that the
&lt;code&gt;widgets_table&lt;/code&gt; change be run first. So let&amp;rsquo;s look at
&lt;code&gt;sql/deploy/widgets_table.sql&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- requires: users_table

CREATE TABLE widgets (
    created_by TEXT NOT NULL REFERENCES users(name),
    name       TEXT NOT NULL
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ah, now here we also require that the &lt;code&gt;users_table&lt;/code&gt; change be deployed first.
Of course, it likely would be, given that it appears under a tag earlier in
the file, but it&amp;rsquo;s best to be safe and explicitly spell out dependencies.
Someone might merge the two tags at some point before release, right?&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;users_table&lt;/code&gt; change has no dependencies, but the later &lt;code&gt;add_user&lt;/code&gt; change
of course does; our &lt;code&gt;sql/deploy/add_user.sql&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- requires: users_table

CREATE OR REPLACE FUNCTION add_user(
    name TEXT
) RETURNS VOID LANGUAGE SQL AS $$
    INSERT INTO users (name) VALUES ($1);
$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Our deployment app can properly resolve these dependencies. Of course, we also
need reversion scripts in the &lt;code&gt;sql/revert&lt;/code&gt; directory. They might look
something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- sql/revert/users_table.sql
DROP TABLE IF EXISTS users;

-- sql/revert/add_widget.sql
DROP FUNCTION IF EXISTS add_widget(text, text);

-- sql/revert/widgets_table.sql
DROP TABLE IF EXISTS widgets;

-- sql/revert/add_user.sql
DROP FUNCTION IF EXISTS add_user(text);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So far so good, right? Our app can resolve dependencies in both directions, so
that if we tell it to revert to &lt;code&gt;beta&lt;/code&gt;, it can do so in the proper order.&lt;/p&gt;

&lt;p&gt;Now, as the deployment app runs the scripts, deploying or reverting changes,
it tracks them and their dependencies in its own metadata table in the
database, not unlike
&lt;a href="http://www.depesz.com/index.php/2010/08/22/versioning/"&gt;Depesz&amp;rsquo;s Versioning&lt;/a&gt;
package. But because dependencies are parsed from comments in the scripts, we
are free to &lt;em&gt;include&lt;/em&gt; the contents of one script in another. For example, say
that we later need to revise the &lt;code&gt;add_widget()&lt;/code&gt; function to log the time a
widget is created. First we add a new script to add the necessary column:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- requires: widgets_table
ALTER TABLE widgets ADD created_at TIMESTAMPTZ;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Call that script &lt;code&gt;sql/deploy/widgets_created_at.sql&lt;/code&gt;. Next we add a script
that changes &lt;code&gt;add_widgets()&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-- requires widgets_created_at
CREATE OR REPLACE FUNCTION add_widget(
    username   TEXT,
    widgetname TEXT
) RETURNS VOID LANGUAGE SQL AS $$
    INSERT INTO widgets (created_by, name, created_at)
    VALUES ($1, $2, NOW());
$$;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Call it &lt;code&gt;sql/deploy/add_widget_v2.sql&lt;/code&gt;. Then update the deployment
configuration file with a new tag and the associated changes:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[delta]
widgets_created_at
add_widget_v2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With me so far? Now, what about reversion? &lt;code&gt;sql/revert/widgets_created_at.sql&lt;/code&gt;
is simple, of course:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ALTER TABLE widgets DROP COLUMN IF EXISTS created_at;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But what should &lt;code&gt;sql/revert/add_widget_v2.sql&lt;/code&gt; look like? Why, to go back to
the first version of &lt;code&gt;add_widget()&lt;/code&gt;, it would be identical to
&lt;code&gt;sql/deploy/add_widget.sql&lt;/code&gt;. But it would be silly to copy the whole file,
wouldn&amp;rsquo;t it? Why duplicate when we can just include?&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;\i sql/deploy/add_widget.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Boom,&lt;/em&gt; we get the reversion script for free. No unnecessary duplication
between deployment and reversion scripts, and all dependencies are nicely
resolved. Plus, the tags in the configuration file make it easy to deploy and
revert change sets as necessary, with dependencies properly followed.&lt;/p&gt;

&lt;h3&gt;There&amp;rsquo;s More!&lt;/h3&gt;

&lt;p&gt;To recap, I had two primary challenges with Depesz&amp;rsquo;s Versioning package to
overcome: inability to easily revert to an earlier implementation; and the
inability to easily include one script in another. Both of course are do-able
with workarounds, but I think that the addition of a deployment configuration
file with tagged sets of changes and the elimination of SQL-embedded
dependency specification overcome these issues much more effectively and
intuitively.&lt;/p&gt;

&lt;p&gt;Still, there are two more challenges I would like to overcome:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It would be nice not to need the configuration file at all. Maintaining
such a thing can be finicky and error-prone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I still had to duplicate the entire &lt;code&gt;add_widget()&lt;/code&gt; function in the
&lt;code&gt;add_widget_v2&lt;/code&gt; script for a very simple change. This means no easy way to
simply see the diff for this change in my VCS. It would be nice not to have
to copy the entire function.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;I think I have solutions for these issues, as well. More in my next post.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/LytHP6uuIHk" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://justatheory.com/computers/databases/simple-sql-change-management.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2011:/computers/programming/perl/perl-advent</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/_EnUR3msWfs/perl-advent.html" />
<title type="text/plain">Today on the Perl Advent Calendar</title>
<dc:subject>Perl</dc:subject>
<dc:subject>Advent+Calendar</dc:subject>
<dc:subject>DBIx::Connector</dc:subject>
<issued>2011-12-22T18:10:00Z</issued>
<modified>2011-12-22T18:10:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p>Hey look everybody, I wrote today's Perl Advent Calendar post, <a href="http://perladvent.org/2011/2011-12-22.html">Less Tedium, More Transactions</a>. Go read it!</p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/_EnUR3msWfs" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/computers/programming/perl/perl-advent.html</feedburner:origLink></entry>

<entry>
<id>tag:justatheory.com,2011:/computers/os/ios/integrate-testflight-sdk</id>
<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/justatheory/atomfull/~3/xM-DskpE8rQ/integrate-testflight-sdk.html" />
<title type="text/plain">How to Integrate the TestFlight SDK into an iOS Project</title>
<dc:subject>iOS</dc:subject>
<dc:subject>TestFlight</dc:subject>
<dc:subject>TestFlightApp</dc:subject>
<dc:subject>SDK</dc:subject>
<dc:subject>Beta</dc:subject>
<dc:subject>release</dc:subject>
<dc:subject>Xcode</dc:subject>
<dc:subject>HOWTO</dc:subject>
<issued>2011-12-18T07:11:00Z</issued>
<modified>2011-12-18T07:11:00Z</modified>
<author>
  <name>David E. Wheeler</name>
</author>
<content type="application/xhtml+xml" xml:base="http://justatheory.com" xml:lang="en-us" xml:space="preserve" mode="xml"><div xmlns="http://www.w3.org/1999/xhtml"><p>I've started using <a href="http://testflightapp.com/">TestFlight</a> to release <a href="http://www.designsceneapp.com/">DesignScene</a> betas to testers. The documentation is thin, so I had to futz a bit, but fortunately it's a pretty simple app, so once I figured out that I just needed to stick to one "Team", I was off and running. And let me tell you, TestFlight is a <em>far</em> easier way to distribute betas than the convoluted methods suggested by Apple. Much more beta user-friendly.</p>

<p>For us developers, the <a href="http://testflightapp.com/sdk/">TestFlight SDK</a> is particularly handy. Add it to your TestFlight-distributed project and get crash reports and remote logging, ask your testers for feedback, and other cool stuff. I've only just started using it, but the immediate diagnostic feedback has already proved invaluable.</p>

<p>Getting the TestFlight SDK to work is dead simple, but it's not supported in App Store distributions. So I wanted to set things up so that it would always be included in beta releases and never in production releases. Getting to that point took a couple of days of futzing, as it's not explicitly supposed by Xcode's UI. The solution I came up with, thanks to <a href="http://stackoverflow.com/questions/8027043/objective-c-having-a-testflight-configuration-to-include-testflight-sdk">this StackOverflow post</a>, is to:</p>

<ul>
<li>Add a "Beta" configuration to complement the default "Release" and "Debug" configurations</li>
<li>Add a preprocessor macro to allow conditional use of the TestFlight SDK</li>
<li>Use the <a href="http://lists.apple.com/archives/xcode-users/2009/Jun/msg00153.html"><code>EXCLUDED_SOURCE_FILE_NAMES</code> setting</a> to exclude the TestFlight library from "Release" builds</li>
</ul>

<p>That last step makes me a <em>bit</em> nervous, but <code>EXCLUDED_SOURCE_FILE_NAMES</code>, while undocumented, seems to be <a href="http://www.google.com/?q=EXCLUDED_SOURCE_FILE_NAMES">reasonably well known</a>. At any rate, I could find no better way to tie the inclusion of a library to a specific configuration, so I'm going with it. Better solutions welcome.</p>

<p>At any rate, here's the step-by-step for Xcode 4.2:</p>

<ul>
<li><a href="https://testflightapp.com/sdk/download/">Download the TestFlight SDK</a> and unpack it.</li>
<li>Drag it into your project. Make sure that "Copy items into destination group's folder" is checked, as is "Create groups for any added folders". Include it in all relevant targets.</li>
<li><p>Create a "Beta" configuration:</p>

<ul>
<li>Click on the app name in the navigator, then on the project name and then the info tab.</li>
<li>Under "Configurations", click the plus sign and select "Duplicate "Release" Configuration"</li>
<li>Type "Beta" to name the new configuration.</li>
</ul>

<p>You should end up with something like this:</p>
<p><img style="float:none;" src="/computers/os/ios/configurations.png" alt="Configurations" /></p>
</li>

<li><p>Create configuration marcos:</p>

<p><img style="float:none;" src="/computers/os/ios/config_config.png" alt="Configurations" /></p>

<ul>
<li>Still in the project settings, go to the "Build Settings" tab.</li>
<li>Search for "preprocessor macros".</li>
<li>Double-click the value section next to the "Preprocessor Macros" label, hit the <code>+</code> button, and enter <code>CONFIGURATION_$(CONFIGURATION)</code>.</li>
</ul>

<p>You should end up with a window like the above. Once you close it, you should see the macros names for each individual configuration, shown here:</p>

<p><img style="float:none;" src="/computers/os/ios/configs.png" alt="Configurations" /></p></li>
<li><p>Add the <code>EXCLUDED_SOURCE_FILE_NAMES</code> build setting.</p>

<ul>
<li>Still in the "Build Settings" tab, click the "Add Build Setting" button in the lower-left corner and select "Add User-Defined Setting".</li>
<li>Input <code>EXCLUDED_SOURCE_FILE_NAMES</code> as the name of the setting.</li>
<li>Open the reveal triangle next to the setting name.</li>
<li>Double-click to the right of "Release".</li>
<li>Enter <code>*libTestFlight.a</code> as the value.</li>
</ul>

<p>You should end up with the value <code>*libTestFlight.a</code> only for the "Release" configuration, as shown here:</p>

<p><img style="float:none;" src="/computers/os/ios/excluded_source_file_names.png" alt="Configurations" /></p></li>
<li><p>Go ahead and use the TestFlight SDK:</p>

<ul>
<li>In your app delegate, add <code>#include "testFlight.h"</code></li>
<li><p>In <code>-application:didFinishLaunchingWithOptions:</code>, just before returning, add these lines:</p>

<pre>#ifdef CONFIGURATION_Beta
    [TestFlight takeOff:@"Insert your Team Token here"];
#endif</pre></li>
</ul></li>
</ul>

<p>Now, when you build or archive with the "Beta" target, the TestFlight SDK will be included and log sessions. But when you build with the "Release" target, TestFlight will neither be bundled or referenced in the app. You can include it anywhere, though, and use any of its features, as long as you do so only within a <code>#ifdef CONFIGURATION_Beta</code> block. Check out the <a href="https://testflightapp.com/sdk/doc/">complete SDK docs</a> for details. Then, get your beta on!</p><xhtml:img xmlns:xhtml="http://www.w3.org/1999/xhtml" src="http://feeds.feedburner.com/~r/justatheory/atomfull/~4/xM-DskpE8rQ" height="1" width="1" /></div></content>
<feedburner:origLink>http://justatheory.com/computers/os/ios/integrate-testflight-sdk.html</feedburner:origLink></entry>


</feed>

