Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Agile Database Development 2ed

Agile Database Development 2ed

Hi, I'm David. I like to write database apps. Just as much as I like to write web apps. (Maybe more!) How? Not by relying on bolted-on, half-baked database integration tools like migrations, I'll tell you that!. Instead, I make extensive use of best-of-breed tools for source control ([Git](http://git-scm.org/)), database unit testing ([pgTAP](http://pgtap.org/)), and database change management and deployment ([Sqitch](http://sqitch.org/). If you'd like to get as much pleasure out of database development as you do application development, join me for this tutorial. We'll develop a sample application using the processes and tools I've come to depend on, and you'll find out whether they might work for you. Either way, I promise it will at least be an amusing use of your time.

[Originally presented at PGCon 2013](http://www.pgcon.org/2013/schedule/events/615.en.html), revised January 2014.

David E. Wheeler

January 09, 2014
Tweet

More Decks by David E. Wheeler

Other Decks in Technology

Transcript

  1. Agile Database Development David E. Wheeler ! ! ! January

    2014 Portland Text: Attribution-Noncommercial-Share Alike 3.0 United States: http:/ /creativecommons.org/licenses/by-nc-sa/3.0/us/ Images licensed independently and © Their respective owners. iovation
  2. Agile Database Development David E. Wheeler ! ! ! January

    2014 Portland Text: Attribution-Noncommercial-Share Alike 3.0 United States: http:/ /creativecommons.org/licenses/by-nc-sa/3.0/us/ Images licensed independently and © Their respective owners. ✘ iovation
  3. David E. Wheeler ! ! ! January 2014 Portland Text:

    Attribution-Noncommercial-Share Alike 3.0 United States: http:/ /creativecommons.org/licenses/by-nc-sa/3.0/us/ Images licensed independently and © Their respective owners.
  4. David E. Wheeler ! ! ! January 2014 Portland License:

    Attribution-Noncommercial-Share Alike 3.0 United States: http:/ /creativecommons.org/licenses/by-nc-sa/3.0/us/
  5. Build My VC-Funded App for Me for Free David E.

    Wheeler ! ! ! January 2014 Portland License: Attribution-Noncommercial-Share Alike 3.0 United States: http:/ /creativecommons.org/licenses/by-nc-sa/3.0/us/ ✔ CEO, Data Architect Agile Assholes, Inc.
  6. This is Genius I had this idea Social networking is

    HAWT Has been for waaaay too long
  7. This is Genius I had this idea Social networking is

    HAWT Has been for waaaay too long The backlash is long overdue
  8. This is Genius I had this idea Social networking is

    HAWT Has been for waaaay too long The backlash is long overdue Getting ahead of the curve
  9. This is Genius I had this idea Social networking is

    HAWT Has been for waaaay too long The backlash is long overdue Getting ahead of the curve Introducing…
  10. How it Works Microblogging platform Everyone follows you New users

    follow everyone Goal: Alienate your followers antisocial network
  11. How it Works Microblogging platform Everyone follows you New users

    follow everyone Goal: Alienate your followers Get them to unfollow you antisocial network
  12. How it Works Microblogging platform Everyone follows you New users

    follow everyone Goal: Alienate your followers Get them to unfollow you Leaderboard: Those with fewest followers antisocial network
  13. Your Task Create the database Use agile development to make

    it right Contribute to VC-funded Corp antisocial network
  14. Your Task Create the database Use agile development to make

    it right Contribute to VC-funded Corp Profit! antisocial network
  15. Your Task Create the database Use agile development to make

    it right Contribute to VC-funded Corp Profit! For VC-funded Corp antisocial network
  16. Job Requirements PostgreSQL Should be installed Git Should be installed

    Sqitch sudo yum install sqitch-pg antisocial network
  17. Job Requirements PostgreSQL Should be installed Git Should be installed

    Sqitch sudo yum install sqitch-pg pgTAP sudo yum install pgtap92 antisocial network
  18. Job Requirements PostgreSQL Should be installed Git Should be installed

    Sqitch sudo yum install sqitch-pg pgTAP sudo yum install pgtap92 pg_prove yum install perl-TAP-Parser-SourceHandler-pgTAP antisocial network
  19. git config -­‐-­‐global user.name 'David  E.  Wheeler' > git config

    -­‐-­‐global user.email [email protected] > > Who Am I? Please use your own name and email.
  20. git config -­‐-­‐global user.name 'David  E.  Wheeler' > git config

    -­‐-­‐global user.email [email protected] > > Who Am I? emacs ~/.gitconfig >
  21. ~/.gitconfig ~/ .gitconfig [user]       name  =  David

     E.  Wheeler         email  =  [email protected] Good for all projects
  22. Create a Remote Create Git project in Stash Or Git

    or BitBucket Wherever you like Record remote URL
  23. mkdir flipr-­‐db > cd flipr-­‐db > git init Initialized empty

    Git repository in flipr-­‐db/.git/ > Gitiup >
  24. mkdir flipr-­‐db > cd flipr-­‐db > git init Initialized empty

    Git repository in flipr-­‐db/.git/ > Gitiup >
  25. mkdir flipr-­‐db > cd flipr-­‐db > git init Initialized empty

    Git repository in flipr-­‐db/.git/ > Gitiup > echo Flipr Database Project > README.md >
  26. mkdir flipr-­‐db > cd flipr-­‐db > git init Initialized empty

    Git repository in flipr-­‐db/.git/ > Gitiup > echo Flipr Database Project > README.md > git add . >
  27. mkdir flipr-­‐db > cd flipr-­‐db > git init Initialized empty

    Git repository in flipr-­‐db/.git/ > Gitiup > echo Flipr Database Project > README.md > git add . > git commit -­‐m 'Initialize repo, add README.' [master (root-­‐commit) 9531fab] Initialize repo, add README. 1 file changed, 1 insertion(+) create mode 100644 README.md >
  28. mkdir flipr-­‐db > cd flipr-­‐db > git init Initialized empty

    Git repository in flipr-­‐db/.git/ > Gitiup > echo Flipr Database Project > README.md > git add . > git commit -­‐m 'Initialize repo, add README.' [master (root-­‐commit) 9531fab] Initialize repo, add README. 1 file changed, 1 insertion(+) create mode 100644 README.md >
  29. mkdir flipr-­‐db > cd flipr-­‐db > git init Initialized empty

    Git repository in flipr-­‐db/.git/ > Gitiup > echo Flipr Database Project > README.md > git add . > git commit -­‐m 'Initialize repo, add README.' [master (root-­‐commit) 9531fab] Initialize repo, add README. 1 file changed, 1 insertion(+) create mode 100644 README.md > We have a Git repo.
  30. git push -­‐u origin master Counting objects: 3, done. Writing

    objects: 100% (3/3), 260 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To https://github.com/theory/agile-­‐flipr.git * [new branch] master -­‐> master Branch master set up to track remote branch master from orig > git remote add origin ssh://[email protected]:7999 > > Origin
  31. git push -­‐u origin master Counting objects: 3, done. Writing

    objects: 100% (3/3), 260 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To https://github.com/theory/agile-­‐flipr.git * [new branch] master -­‐> master Branch master set up to track remote branch master from orig > git remote add origin ssh://[email protected]:7999 > > Origin
  32. git push -­‐u origin master Counting objects: 3, done. Writing

    objects: 100% (3/3), 260 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To https://github.com/theory/agile-­‐flipr.git * [new branch] master -­‐> master Branch master set up to track remote branch master from orig > git remote add origin ssh://[email protected]:7999 > > Origin
  33. git push -­‐u origin master Counting objects: 3, done. Writing

    objects: 100% (3/3), 260 bytes, done. Total 3 (delta 0), reused 0 (delta 0) To https://github.com/theory/agile-­‐flipr.git * [new branch] master -­‐> master Branch master set up to track remote branch master from orig > git remote add origin ssh://[email protected]:7999 > > Origin
  34. git remote add upstream \ https://github.com/theory/agile-­‐flipr.git > Swimming Upstream >

    git fetch upstream From https://github.com/theory/agile-­‐flipr * [new branch] change_pass -­‐> upstream/change_pass * [new branch] flips -­‐> upstream/flips * [new branch] master -­‐> upstream/master * [new branch] userfuncs -­‐> upstream/userfuncs * [new branch] users -­‐> upstream/users From https://github.com/theory/agile-­‐flipr * [new tag] insert_user2 -­‐> insert_user2 * [new tag] reltag -­‐> reltag * [new tag] v1.0.0-­‐r1 -­‐> v1.0.0-­‐r1 >
  35. git remote add upstream \ https://github.com/theory/agile-­‐flipr.git > Swimming Upstream >

    git fetch upstream From https://github.com/theory/agile-­‐flipr * [new branch] change_pass -­‐> upstream/change_pass * [new branch] flips -­‐> upstream/flips * [new branch] master -­‐> upstream/master * [new branch] userfuncs -­‐> upstream/userfuncs * [new branch] users -­‐> upstream/users From https://github.com/theory/agile-­‐flipr * [new tag] insert_user2 -­‐> insert_user2 * [new tag] reltag -­‐> reltag * [new tag] v1.0.0-­‐r1 -­‐> v1.0.0-­‐r1 > For resetting later
  36. Git? Manage tree of files over time Distributed development Commit

    changes locally Merge to remote (origin, upstream) antisocial network
  37. Git? Manage tree of files over time Distributed development Commit

    changes locally Merge to remote (origin, upstream) Speedy, responsive antisocial network
  38. Git? Manage tree of files over time Distributed development Commit

    changes locally Merge to remote (origin, upstream) Speedy, responsive Flexible, robust antisocial network
  39. Why Git? Anyone can clone Complete repository copy Cheap branching

    Make and test local changes antisocial network
  40. Why Git? Anyone can clone Complete repository copy Cheap branching

    Make and test local changes Submit patches, pull requests antisocial network
  41. Why Git? Anyone can clone Complete repository copy Cheap branching

    Make and test local changes Submit patches, pull requests Pull from upstream antisocial network
  42. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: antisocial network
  43. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID antisocial network
  44. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID parent ID antisocial network
  45. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID parent ID author antisocial network
  46. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID parent ID author committer antisocial network
  47. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID parent ID author committer message antisocial network
  48. > git cat-­‐file commit HEAD tree 8b0955fd015782a26953e20d41db5fadbb347c14 parent 0f38581c4d19313d910c2080b3470cae07f3694e author

    David Wheeler <[email protected]> 1367971872 -­‐0700 committer David Wheeler <[email protected]> 1367971872 -­‐0700 Get through "Ship it!". > Making a hash of it
  49. > git cat-­‐file commit HEAD tree 8b0955fd015782a26953e20d41db5fadbb347c14 parent 0f38581c4d19313d910c2080b3470cae07f3694e author

    David Wheeler <[email protected]> 1367971872 -­‐0700 committer David Wheeler <[email protected]> 1367971872 -­‐0700 Get through "Ship it!". > Making a hash of it
  50. > git cat-­‐file commit HEAD tree 8b0955fd015782a26953e20d41db5fadbb347c14 parent 0f38581c4d19313d910c2080b3470cae07f3694e author

    David Wheeler <[email protected]> 1367971872 -­‐0700 committer David Wheeler <[email protected]> 1367971872 -­‐0700 Get through "Ship it!". > Making a hash of it
  51. > git cat-­‐file commit HEAD tree 8b0955fd015782a26953e20d41db5fadbb347c14 parent 0f38581c4d19313d910c2080b3470cae07f3694e author

    David Wheeler <[email protected]> 1367971872 -­‐0700 committer David Wheeler <[email protected]> 1367971872 -­‐0700 Get through "Ship it!". > Making a hash of it
  52. > git cat-­‐file commit HEAD tree 8b0955fd015782a26953e20d41db5fadbb347c14 parent 0f38581c4d19313d910c2080b3470cae07f3694e author

    David Wheeler <[email protected]> 1367971872 -­‐0700 committer David Wheeler <[email protected]> 1367971872 -­‐0700 Get through "Ship it!". > Making a hash of it
  53. > git cat-­‐file commit HEAD tree 8b0955fd015782a26953e20d41db5fadbb347c14 parent 0f38581c4d19313d910c2080b3470cae07f3694e author

    David Wheeler <[email protected]> 1367971872 -­‐0700 committer David Wheeler <[email protected]> 1367971872 -­‐0700 Get through "Ship it!". > Making a hash of it
  54. SHAzam! Each commit (except first) includes parent Can trace from

    any commit to the beginning antisocial network
  55. SHAzam! Each commit (except first) includes parent Can trace from

    any commit to the beginning Tampering (corruption) detectable antisocial network
  56. SHAzam! Each commit (except first) includes parent Can trace from

    any commit to the beginning Tampering (corruption) detectable Because the hash will be wrong antisocial network
  57. SHAzam! Each commit (except first) includes parent Can trace from

    any commit to the beginning Tampering (corruption) detectable Because the hash will be wrong Linus Torvalds’s “greatest invention” http:/ /perl.plover.com/yak/git/ antisocial network
  58. antisocial network Your Turn Configure Git Initialize repository Add origin

    remote Add upstream remote https:/ /github.com/ theory/agile-flipr.git
  59.    sqitch  config  -­‐-­‐user  user.name  'David  E.  Wheeler' >  

     sqitch  config  -­‐-­‐user  user.email  [email protected] > Who am I again? >
  60.    sqitch  config  -­‐-­‐user  user.name  'David  E.  Wheeler' >  

     sqitch  config  -­‐-­‐user  user.email  [email protected] > > Who am I again? >    emacs  ~/.sqitch/sqitch.conf >
  61. ~/.sqitch/sqitc ~/ .sqitch/sqitch.conf [user]       name  =  David

     E.  Wheeler         email  =  [email protected]   Good for all projects
  62. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch
  63. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch
  64. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch Use remote URL
  65. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch
  66. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch
  67. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch
  68. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch
  69. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch
  70. > sqitch -­‐-­‐engine pg init flipr \ -­‐-­‐uri https://github.com/theory/agile-­‐flipr Created

    sqitch.conf Created sqitch.plan Created deploy/ Created revert/ Created verify/ > > Scratch that Sqitch    emacs  sqitch.conf >
  71. sqitch.conf sqitch.conf [core]     engine  =  pg    

    #  plan_file  =  sqitch.plan     #  top_dir  =  .     #  deploy_dir  =  deploy     #  revert_dir  =  revert     #  verify_dir  =  verify     #  extension  =  sql   #  [core  "pg"]     #  target  =  db:pg:     #  registry  =  sqitch     #  client  =  psql  
  72. sqitch.conf sqitch.conf [core]     engine  =  pg    

    #  plan_file  =  sqitch.plan     #  top_dir  =  .     #  deploy_dir  =  deploy     #  revert_dir  =  revert     #  verify_dir  =  verify     #  extension  =  sql   #  [core  "pg"]     #  target  =  db:pg:     #  registry  =  sqitch     #  client  =  psql   --engine pg
  73. Make It So >    git  add  . >  git

     commit  -­‐m  'Initialize  Sqitch  configuration.' [master  e56e7c8]  Initialize  Sqitch  configuration.  2  files  changed,  16  insertions(+)  create  mode  100644  sqitch.conf  create  mode  100644  sqitch.plan >
  74. Make It So >    git  add  . >  git

     commit  -­‐m  'Initialize  Sqitch  configuration.' [master  e56e7c8]  Initialize  Sqitch  configuration.  2  files  changed,  16  insertions(+)  create  mode  100644  sqitch.conf  create  mode  100644  sqitch.plan >
  75. Make It So >    git  add  . >  git

     commit  -­‐m  'Initialize  Sqitch  configuration.' [master  e56e7c8]  Initialize  Sqitch  configuration.  2  files  changed,  16  insertions(+)  create  mode  100644  sqitch.conf  create  mode  100644  sqitch.plan >
  76. Make It So >    git  add  . >  git

     commit  -­‐m  'Initialize  Sqitch  configuration.' [master  e56e7c8]  Initialize  Sqitch  configuration.  2  files  changed,  16  insertions(+)  create  mode  100644  sqitch.conf  create  mode  100644  sqitch.plan >    git  push Counting  objects:  5,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (4/4),  done. Writing  objects:  100%  (4/4),  555  bytes,  done. Total  4  (delta  0),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      9531fab..e56e7c8    master  -­‐>  master >
  77. Make It So >    git  add  . >  git

     commit  -­‐m  'Initialize  Sqitch  configuration.' [master  e56e7c8]  Initialize  Sqitch  configuration.  2  files  changed,  16  insertions(+)  create  mode  100644  sqitch.conf  create  mode  100644  sqitch.plan >    git  push Counting  objects:  5,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (4/4),  done. Writing  objects:  100%  (4/4),  555  bytes,  done. Total  4  (delta  0),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      9531fab..e56e7c8    master  -­‐>  master >
  78. Make It So >    git  add  . >  git

     commit  -­‐m  'Initialize  Sqitch  configuration.' [master  e56e7c8]  Initialize  Sqitch  configuration.  2  files  changed,  16  insertions(+)  create  mode  100644  sqitch.conf  create  mode  100644  sqitch.plan >    git  push Counting  objects:  5,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (4/4),  done. Writing  objects:  100%  (4/4),  555  bytes,  done. Total  4  (delta  0),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      9531fab..e56e7c8    master  -­‐>  master >
  79.    git  log commit  e56e7c88d85fa84e7f726df36bd0ae7a74a618aa Author:  David  E.  Wheeler  <[email protected]>

    Date:      Tue  Jan  7  15:44:40  2014  -­‐0800        Initialize  Sqitch  configuration. commit  9531fab56ecd9abe77636045584d1dc1b74fbc7b Author:  David  E.  Wheeler  <[email protected]> Date:      Tue  Jan  7  15:18:30  2014  -­‐0800        Initialize  repo,  add  README. Where’ve We Been? >
  80.    git  log commit  e56e7c88d85fa84e7f726df36bd0ae7a74a618aa Author:  David  E.  Wheeler  <[email protected]>

    Date:      Tue  Jan  7  15:44:40  2014  -­‐0800        Initialize  Sqitch  configuration. commit  9531fab56ecd9abe77636045584d1dc1b74fbc7b Author:  David  E.  Wheeler  <[email protected]> Date:      Tue  Jan  7  15:18:30  2014  -­‐0800        Initialize  repo,  add  README. Where’ve We Been? >
  81.    git  log commit  e56e7c88d85fa84e7f726df36bd0ae7a74a618aa Author:  David  E.  Wheeler  <[email protected]>

    Date:      Tue  Jan  7  15:44:40  2014  -­‐0800        Initialize  Sqitch  configuration. commit  9531fab56ecd9abe77636045584d1dc1b74fbc7b Author:  David  E.  Wheeler  <[email protected]> Date:      Tue  Jan  7  15:18:30  2014  -­‐0800        Initialize  repo,  add  README. Where’ve We Been? >
  82. First Change >  sqitch  add  appschema  -­‐n  'Adds  flipr  app

     schema.' Created  deploy/appschema.sql Created  revert/appschema.sql Created  verify/appschema.sql Added  "appschema"  to  sqitch.plan > >
  83. First Change >  sqitch  add  appschema  -­‐n  'Adds  flipr  app

     schema.' Created  deploy/appschema.sql Created  revert/appschema.sql Created  verify/appschema.sql Added  "appschema"  to  sqitch.plan > >
  84. First Change >  sqitch  add  appschema  -­‐n  'Adds  flipr  app

     schema.' Created  deploy/appschema.sql Created  revert/appschema.sql Created  verify/appschema.sql Added  "appschema"  to  sqitch.plan > >
  85. First Change >  sqitch  add  appschema  -­‐n  'Adds  flipr  app

     schema.' Created  deploy/appschema.sql Created  revert/appschema.sql Created  verify/appschema.sql Added  "appschema"  to  sqitch.plan > >    emacs  deploy/appschema.sql >
  86. >  sqitch  add  appschema  -­‐n  'Adds  flipr  app  schema.'  

    Created  deploy/appschema.sql   Created  revert/appschema.sql   Created  verify/appschema.sql   Added  "appschema"  to  sqitch.plan   > First Change >    emacs  deploy/appschema.sql   >
  87. >  sqitch  add  appschema  -­‐n  'Adds  flipr  app  schema.'  

    Created  deploy/appschema.sql   Created  revert/appschema.sql   Created  verify/appschema.sql   Added  "appschema"  to  sqitch.plan   > First Change >    emacs  deploy/appschema.sql   >    emacs  revert/appschema.sql >
  88.    createdb  flipr_test >    createuser  -­‐s  -­‐U  postgres  `whoami`

    >    sqitch  deploy  db:pg:flipr_test Adding  registry  tables  to  db:pg:flipr_test Deploying  to  db:pg:flipr_test    +  appschema  ..  ok > Make it So! >
  89.    createdb  flipr_test >    createuser  -­‐s  -­‐U  postgres  `whoami`

    >    sqitch  deploy  db:pg:flipr_test Adding  registry  tables  to  db:pg:flipr_test Deploying  to  db:pg:flipr_test    +  appschema  ..  ok > Make it So! >
  90.    createdb  flipr_test >    createuser  -­‐s  -­‐U  postgres  `whoami`

    >    sqitch  deploy  db:pg:flipr_test Adding  registry  tables  to  db:pg:flipr_test Deploying  to  db:pg:flipr_test    +  appschema  ..  ok > Make it So! >
  91.    createdb  flipr_test >    createuser  -­‐s  -­‐U  postgres  `whoami`

    >    sqitch  deploy  db:pg:flipr_test Adding  registry  tables  to  db:pg:flipr_test Deploying  to  db:pg:flipr_test    +  appschema  ..  ok > Make it So! >
  92.    createdb  flipr_test >    createuser  -­‐s  -­‐U  postgres  `whoami`

    >    sqitch  deploy  db:pg:flipr_test Adding  registry  tables  to  db:pg:flipr_test Deploying  to  db:pg:flipr_test    +  appschema  ..  ok > Make it So!    psql  -­‐d  flipr_test  -­‐c  '\dn  flipr' List  of  schemas  Name    |  Owner   -­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐  flipr  |  david > >
  93.    createdb  flipr_test >    createuser  -­‐s  -­‐U  postgres  `whoami`

    >    sqitch  deploy  db:pg:flipr_test Adding  registry  tables  to  db:pg:flipr_test Deploying  to  db:pg:flipr_test    +  appschema  ..  ok > Make it So!    psql  -­‐d  flipr_test  -­‐c  '\dn  flipr' List  of  schemas  Name    |  Owner   -­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐  flipr  |  david > Trust, but verify >
  94.    createdb  flipr_test >    createuser  -­‐s  -­‐U  postgres  `whoami`

    >    sqitch  deploy  db:pg:flipr_test Adding  registry  tables  to  db:pg:flipr_test Deploying  to  db:pg:flipr_test    +  appschema  ..  ok > Make it So!    psql  -­‐d  flipr_test  -­‐c  '\dn  flipr' List  of  schemas  Name    |  Owner   -­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐  flipr  |  david >    emacs  verify/appschema.sql >
  95. -­‐-­‐  Verify  appschema   ! BEGIN;   ! ! !

    ROLLBACK; verify/appschem verify/appschema.sql -­‐-­‐  XXX  Add  verifications  here.
  96. -­‐-­‐  Verify  appschema   ! BEGIN;   ! ! !

    ROLLBACK; SELECT  pg_catalog.has_schema_privilege('nada',  'usage'); verify/appschem verify/appschema.sql
  97. -­‐-­‐  Verify  appschema   ! BEGIN;   ! ! !

    ROLLBACK; SELECT  pg_catalog.has_schema_privilege('nada',  'usage'); verify/appschem verify/appschema.sql Let’s try it, first
  98.    emacs  verify/appschema.sql   >      sqitch  verify  db:pg:flipr_test

    Verifying  db:pg:flipr_test    *  appschema  ..  psql:verify/appschema.sql:5:   ERROR:    schema  "nada"  does  not  exist #  Verify  script  "verify/appschema.sql"  failed. not  ok Verify  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Changes:  1 Errors:    1 Verify  failed > Trust, But Verify >
  99.    emacs  verify/appschema.sql   >      sqitch  verify  db:pg:flipr_test

    Verifying  db:pg:flipr_test    *  appschema  ..  psql:verify/appschema.sql:5:   ERROR:    schema  "nada"  does  not  exist #  Verify  script  "verify/appschema.sql"  failed. not  ok Verify  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Changes:  1 Errors:    1 Verify  failed > Trust, But Verify >
  100.    emacs  verify/appschema.sql   >      sqitch  verify  db:pg:flipr_test

    Verifying  db:pg:flipr_test    *  appschema  ..  psql:verify/appschema.sql:5:   ERROR:    schema  "nada"  does  not  exist #  Verify  script  "verify/appschema.sql"  failed. not  ok Verify  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Changes:  1 Errors:    1 Verify  failed > Trust, But Verify >
  101.    emacs  verify/appschema.sql   >      sqitch  verify  db:pg:flipr_test

    Verifying  db:pg:flipr_test    *  appschema  ..  psql:verify/appschema.sql:5:   ERROR:    schema  "nada"  does  not  exist #  Verify  script  "verify/appschema.sql"  failed. not  ok Verify  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Changes:  1 Errors:    1 Verify  failed > Trust, But Verify >
  102.    sqitch  verify  db:pg:flipr_test Verifying  db:pg:flipr_test    *  appschema  ..

     ok Verify  successful >    perl  -­‐i  -­‐pe  's/nada/flipr/'  verify/appschema.sql >   Trust, But Verify > Mo betta.
  103. > How’s it Look? >  sqitch  status  db:pg:flipr_test #  On

     database  db:pg:flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:07:22  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date)
  104. > How’s it Look? >  sqitch  status  db:pg:flipr_test #  On

     database  db:pg:flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:07:22  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date)
  105. > How’s it Look? >  sqitch  status  db:pg:flipr_test #  On

     database  db:pg:flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:07:22  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date)
  106. > How’s it Look? >  sqitch  status  db:pg:flipr_test #  On

     database  db:pg:flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:07:22  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date)
  107. > How’s it Look? >  sqitch  status  db:pg:flipr_test #  On

     database  db:pg:flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:07:22  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date)
  108. > How’s it Look? >  sqitch  status  db:pg:flipr_test #  On

     database  db:pg:flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:07:22  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date)
  109. >  sqitch  revert  db:pg:flipr_test            

          Revert  all  changes  from  db:pg:flipr_test?  [Yes]   Go Back > ▮
  110. >  sqitch  revert  db:pg:flipr_test            

          Revert  all  changes  from  db:pg:flipr_test?  [Yes]   Go Back > ▮
  111. >  sqitch  revert  db:pg:flipr_test            

          Revert  all  changes  from  db:pg:flipr_test?  [Yes]   Go Back >    -­‐  appschema  ..  ok >
  112. >  sqitch  revert  db:pg:flipr_test            

          Revert  all  changes  from  db:pg:flipr_test?  [Yes]   Go Back >    psql  -­‐d  flipr_test  -­‐c  '\dn  flipr'                        List  of  roles List  of  schemas  Name  |  Owner   -­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐ >    -­‐  appschema  ..  ok >
  113. >  sqitch  revert  db:pg:flipr_test            

          Revert  all  changes  from  db:pg:flipr_test?  [Yes]   Go Back >    psql  -­‐d  flipr_test  -­‐c  '\dn  flipr'                        List  of  roles List  of  schemas  Name  |  Owner   -­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐ >    -­‐  appschema  ..  ok >      sqitch  status  db:pg:flipr_test #  On  database  db:pg:flipr_test No  changes  deployed >
  114. History > >  sqitch  log  db:pg:flipr_test On  database  flipr_test Revert

     e01feba4bb79607339a13981574855b6e2aa7526 Name:            appschema Committer:  David  E.  Wheeler  <[email protected]> Date:            2014-­‐01-­‐07  16:07:44  -­‐0800        Adds  flipr  app  schema. Deploy  e01feba4bb79607339a13981574855b6e2aa7526 Name:            appschema Committer:  David  E.  Wheeler  <[email protected]> Date:            2014-­‐01-­‐07  16:07:22  -­‐0800        Adds  flipr  app  schema.
  115. History > >  sqitch  log  db:pg:flipr_test On  database  flipr_test Revert

     e01feba4bb79607339a13981574855b6e2aa7526 Name:            appschema Committer:  David  E.  Wheeler  <[email protected]> Date:            2014-­‐01-­‐07  16:07:44  -­‐0800        Adds  flipr  app  schema. Deploy  e01feba4bb79607339a13981574855b6e2aa7526 Name:            appschema Committer:  David  E.  Wheeler  <[email protected]> Date:            2014-­‐01-­‐07  16:07:22  -­‐0800        Adds  flipr  app  schema.
  116. History > >  sqitch  log  db:pg:flipr_test On  database  flipr_test Revert

     e01feba4bb79607339a13981574855b6e2aa7526 Name:            appschema Committer:  David  E.  Wheeler  <[email protected]> Date:            2014-­‐01-­‐07  16:07:44  -­‐0800        Adds  flipr  app  schema. Deploy  e01feba4bb79607339a13981574855b6e2aa7526 Name:            appschema Committer:  David  E.  Wheeler  <[email protected]> Date:            2014-­‐01-­‐07  16:07:22  -­‐0800        Adds  flipr  app  schema.
  117. Commit It! >    git  add  . >  git  commit

     -­‐m  'Add  flipr  schema.' [master  07548c2]  Add  flipr  schema.  4  files  changed,  22  insertions(+)  create  mode  100644  deploy/appschema.sql  create  mode  100644  revert/appschema.sql  create  mode  100644  verify/appschema.sql >
  118. Commit It! >    git  add  . >  git  commit

     -­‐m  'Add  flipr  schema.' [master  07548c2]  Add  flipr  schema.  4  files  changed,  22  insertions(+)  create  mode  100644  deploy/appschema.sql  create  mode  100644  revert/appschema.sql  create  mode  100644  verify/appschema.sql >
  119. Commit It! >    git  add  . >  git  commit

     -­‐m  'Add  flipr  schema.' [master  07548c2]  Add  flipr  schema.  4  files  changed,  22  insertions(+)  create  mode  100644  deploy/appschema.sql  create  mode  100644  revert/appschema.sql  create  mode  100644  verify/appschema.sql >
  120. Commit It! >    git  add  . >  git  commit

     -­‐m  'Add  flipr  schema.' [master  07548c2]  Add  flipr  schema.  4  files  changed,  22  insertions(+)  create  mode  100644  deploy/appschema.sql  create  mode  100644  revert/appschema.sql  create  mode  100644  verify/appschema.sql >    git  push Counting  objects:  11,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (6/6),  done. Writing  objects:  100%  (9/9),  950  bytes,  done. Total  9  (delta  0),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      e56e7c8..07548c2    master  -­‐>  master
  121. Commit It! >    git  add  . >  git  commit

     -­‐m  'Add  flipr  schema.' [master  07548c2]  Add  flipr  schema.  4  files  changed,  22  insertions(+)  create  mode  100644  deploy/appschema.sql  create  mode  100644  revert/appschema.sql  create  mode  100644  verify/appschema.sql >    git  push Counting  objects:  11,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (6/6),  done. Writing  objects:  100%  (9/9),  950  bytes,  done. Total  9  (delta  0),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      e56e7c8..07548c2    master  -­‐>  master
  122. >  sqitch  deploy  db:pg:flipr_test  -­‐-­‐verify Deploying  changes  to  flipr_test  

     +  appschema  ..  ok > Redeploy >    psql  -­‐d  flipr_test  -­‐c  '\dn  flipr' List  of  schemas  Name    |  Owner   -­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐  flipr  |  david >
  123. >  sqitch  status  db:pg:flipr_test #  On  database  db:pg:flipr_test #  Project:

       flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:13:50  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date) > Status Update >
  124. On Target >    sqitch  target  add  flipr_test  db:pg:flipr_test >

       sqitch  config  core.pg.target  flipr_test >
  125. On Target >    sqitch  target  add  flipr_test  db:pg:flipr_test >

       sqitch  config  core.pg.target  flipr_test > >  sqitch  status #  On  database  flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:13:50  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date) > No URI
  126. On Target >    sqitch  target  add  flipr_test  db:pg:flipr_test >

       sqitch  config  core.pg.target  flipr_test > >  sqitch  config  -­‐-­‐bool  deploy.verify  true >  sqitch  config  -­‐-­‐bool  rebase.verify  true > >  sqitch  status #  On  database  flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:13:50  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Nothing  to  deploy  (up-­‐to-­‐date) > Always verify.
  127. Commit Config >    git  add  . >  git  commit

     -­‐m  'Add  target  and  verify  to  config.' [master  099b677]  Add  target  and  verify  to  config.  1  file  changed,  4  insertions(+) >
  128. Commit Config >    git  add  . >  git  commit

     -­‐m  'Add  target  and  verify  to  config.' [master  099b677]  Add  target  and  verify  to  config.  1  file  changed,  4  insertions(+) >
  129. Commit Config >    git  push Counting  objects:  5,  done.

    Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (3/3),  done. Writing  objects:  100%  (3/3),  361  bytes,  done. Total  3  (delta  2),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      07548c2..099b677    master  -­‐>  master >    git  add  . >  git  commit  -­‐m  'Add  target  and  verify  to  config.' [master  099b677]  Add  target  and  verify  to  config.  1  file  changed,  4  insertions(+) >
  130. SQL Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    hard to track No VCS awareness ——————————————— antisocial network
  131. SQL Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    hard to track No VCS awareness ——————————————— ——————————————————— antisocial network
  132. SQL Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    hard to track No VCS awareness ——————————————— ——————————————————— antisocial network
  133. Sqitch Philosophy No opinions Native scripting (psql, sqlite3, SQL*Plus) Cross-project

    dependency resolution Distribution bundling antisocial network
  134. Sqitch Philosophy No opinions Native scripting (psql, sqlite3, SQL*Plus) Cross-project

    dependency resolution Distribution bundling Integrated verification testing antisocial network
  135. Sqitch Philosophy No opinions Native scripting (psql, sqlite3, SQL*Plus) Cross-project

    dependency resolution Distribution bundling Integrated verification testing No numbering antisocial network
  136. Sqitch Philosophy No opinions Native scripting (psql, sqlite3, SQL*Plus) Cross-project

    dependency resolution Distribution bundling Integrated verification testing No numbering Reliable sequential deployment ordering antisocial network
  137. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify antisocial network
  138. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: antisocial network
  139. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: Project antisocial network
  140. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: Project Name antisocial network
  141. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: Project Name Parent ID antisocial network
  142. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: Project Name Parent ID Planner… antisocial network
  143. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  144. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. > Symbolic Sqitch tag
  145. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  146. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  147. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  148. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  149. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  150. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  151. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  152. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  153. SHAsome >    sqitch  show  change  @HEAD project  pgxn_manager uri

     https://github.com/pgxn/pgxn-­‐manager.git change  distview parent  39d7e105cb00e1798cfabb45f4b74cbc14a82513 planner  David  E.  Wheeler  <[email protected]> date  2013-­‐02-­‐08T23:51:19Z requires    +  roles    +  pgxn_core:types    +  distributions    +  extensions Adds  the  filters  table. >
  154. SHApay! Each change (except first) includes parent Can trace from

    any change to the beginning antisocial network
  155. SHApay! Each change (except first) includes parent Can trace from

    any change to the beginning Change tampering (corruption) detectable antisocial network
  156. SHApay! Each change (except first) includes parent Can trace from

    any change to the beginning Change tampering (corruption) detectable Because the hash will be wrong antisocial network
  157. SHApay! Each change (except first) includes parent Can trace from

    any change to the beginning Change tampering (corruption) detectable Because the hash will be wrong Stole Linus Torvalds’s “greatest invention” antisocial network
  158. antisocial network Your Turn Configure Sqitch Initialize project Add appschema

    change Deploy/Revert Commit/push https:/ /github.com/ theory/agile-flipr.git
  159. test/appschema. pgTAP Basics SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF

     NOT  EXISTS  pgtap; RESET  client_min_messages; BEGIN; SELECT  plan(1); SELECT  has_schema('nada'); SELECT  finish(); ROLLBACK;
  160. test/appschema. pgTAP Basics SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF

     NOT  EXISTS  pgtap; RESET  client_min_messages; BEGIN; SELECT  plan(1); SELECT  has_schema('nada'); SELECT  finish(); ROLLBACK;
  161. test/appschema. pgTAP Basics SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF

     NOT  EXISTS  pgtap; RESET  client_min_messages; BEGIN; SELECT  plan(1); SELECT  has_schema('nada'); SELECT  finish(); ROLLBACK;
  162. test/appschema. pgTAP Basics SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF

     NOT  EXISTS  pgtap; RESET  client_min_messages; BEGIN; SELECT  plan(1); SELECT  has_schema('nada'); SELECT  finish(); ROLLBACK;
  163. test/appschema. pgTAP Basics SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF

     NOT  EXISTS  pgtap; RESET  client_min_messages; BEGIN; SELECT  plan(1); SELECT  has_schema('nada'); SELECT  finish(); ROLLBACK;
  164. test/appschema. pgTAP Basics SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF

     NOT  EXISTS  pgtap; RESET  client_min_messages; BEGIN; SELECT  plan(1); SELECT  has_schema('nada'); SELECT  finish(); ROLLBACK;
  165. test/appschema. pgTAP Basics SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF

     NOT  EXISTS  pgtap; RESET  client_min_messages; BEGIN; SELECT  plan(1); SELECT  has_schema('nada'); SELECT  finish(); ROLLBACK;
  166. > Run the Test pg_prove -­‐v -­‐d flipr_test test/appschema.sql test/appschema.sql

    .. 1..1 not ok 1 -­‐ Schema nada should exist # Failed test 1: "Schema nada should exist" # Looks like you failed 1 test of 1 Failed 1/1 subtests Test Summary Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/appschema.sql (Wstat: 0 Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL
  167. > Run the Test pg_prove -­‐v -­‐d flipr_test test/appschema.sql test/appschema.sql

    .. 1..1 not ok 1 -­‐ Schema nada should exist # Failed test 1: "Schema nada should exist" # Looks like you failed 1 test of 1 Failed 1/1 subtests Test Summary Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/appschema.sql (Wstat: 0 Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL
  168. > Run the Test pg_prove -­‐v -­‐d flipr_test test/appschema.sql test/appschema.sql

    .. 1..1 not ok 1 -­‐ Schema nada should exist # Failed test 1: "Schema nada should exist" # Looks like you failed 1 test of 1 Failed 1/1 subtests Test Summary Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/appschema.sql (Wstat: 0 Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL
  169. perl -­‐i -­‐pe 's/nada/flipr/' test/appschema.sql > First Pass > >

    pg_prove -­‐v -­‐d flipr_test test/appschema.sql test/appschema.sql .. 1..1 ok 1 -­‐ Schema flipr should exist ok All tests successful. Files=1, Tests=1, 0 wallclock secs Result: PASS
  170. perl -­‐i -­‐pe 's/nada/flipr/' test/appschema.sql > First Pass W00t! >

    > pg_prove -­‐v -­‐d flipr_test test/appschema.sql test/appschema.sql .. 1..1 ok 1 -­‐ Schema flipr should exist ok All tests successful. Files=1, Tests=1, 0 wallclock secs Result: PASS
  171. git add . > git commit -­‐m 'Add appschema test.'

    [master 92bd85c] Add appschema test. 1 file changed, 14 insertions(+) create mode 100644 test/appschema.sql > Pass it On >
  172. git add . > git commit -­‐m 'Add appschema test.'

    [master 92bd85c] Add appschema test. 1 file changed, 14 insertions(+) create mode 100644 test/appschema.sql > Pass it On > git push Counting objects: 5, done. Delta compression using up to 4 threads. Compressing objects: 100% (3/3), done. Writing objects: 100% (4/4), 486 bytes, done. Total 4 (delta 1), reused 0 (delta 0) To ssh://[email protected]:7999/~david.w 099b677..c965fb6 master -­‐> master >
  173. What does that mean in practice? Test output easy to

    interpret What is TAP? antisocial network
  174. What does that mean in practice? Test output easy to

    interpret By humans What is TAP? antisocial network
  175. What does that mean in practice? Test output easy to

    interpret By humans By computers What is TAP? antisocial network
  176. What does that mean in practice? Test output easy to

    interpret By humans By computers pg_prove (the harness) What is TAP? antisocial network
  177. What does that mean in practice? Test output easy to

    interpret By humans By computers pg_prove (the harness) By aliens What is TAP? antisocial network
  178. What does that mean in practice? Test output easy to

    interpret By humans By computers pg_prove (the harness) By aliens By gum! What is TAP? antisocial network
  179. What’s the plan, man? Includes Test controls: plan() — How

    many tests? no_plan() — Unknown number of tests antisocial network
  180. What’s the plan, man? Includes Test controls: plan() — How

    many tests? no_plan() — Unknown number of tests diag() — Diagnostic output antisocial network
  181. What’s the plan, man? Includes Test controls: plan() — How

    many tests? no_plan() — Unknown number of tests diag() — Diagnostic output finish() — Test finished, report! antisocial network
  182. Scalarly Includes simple scalar test functions: ok() — Boolean is()

    — Value comparison isnt() — NOT is() antisocial network
  183. Scalarly Includes simple scalar test functions: ok() — Boolean is()

    — Value comparison isnt() — NOT is() cmp_ok() — Compare with specific operator antisocial network
  184. Scalarly Includes simple scalar test functions: ok() — Boolean is()

    — Value comparison isnt() — NOT is() cmp_ok() — Compare with specific operator matches() — Regex comparison antisocial network
  185. Scalarly Includes simple scalar test functions: ok() — Boolean is()

    — Value comparison isnt() — NOT is() cmp_ok() — Compare with specific operator matches() — Regex comparison imatches() — Case-insensitive regex comparison antisocial network
  186. It’s All Relative Includes functions for testing relations: results_eq() —

    Ordered results set_eq() — Set of values antisocial network
  187. It’s All Relative Includes functions for testing relations: results_eq() —

    Ordered results set_eq() — Set of values bag_eq() — Bag of values antisocial network
  188. It’s All Relative Includes functions for testing relations: results_eq() —

    Ordered results set_eq() — Set of values bag_eq() — Bag of values row_eq() — Single row antisocial network
  189. It’s All Relative Includes functions for testing relations: results_eq() —

    Ordered results set_eq() — Set of values bag_eq() — Bag of values row_eq() — Single row is_empty() — No results antisocial network
  190. I’m Okay, You’re Okay throws_ok() — Throws an exception throws_like()

    — Exception matches regex skip() — Skip a subset of tests antisocial network
  191. I’m Okay, You’re Okay throws_ok() — Throws an exception throws_like()

    — Exception matches regex skip() — Skip a subset of tests todo() — Expect subset of tests to fail antisocial network
  192. Schematics has_table(), has_view(), has_function(), etc. columns_are(), has_pk(), fk_ok(), etc. col_type_is(),

    col_not_null(), col_default_is() So, so much more… antisocial network
  193. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress antisocial network
  194. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions antisocial network
  195. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions Role and privilege assertions antisocial network
  196. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions Role and privilege assertions http:/ /pgtap.org/ antisocial network
  197. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions Role and privilege assertions http:/ /pgtap.org/ http:/ /pgxn.org/extension/pgtap/ antisocial network
  198. antisocial network Let’s do it! Create appschema test Use pgTAP

    Run test with pg_prove Make it fail Make it pass!
  199. antisocial network Let’s do it! Create appschema test Use pgTAP

    Run test with pg_prove Make it fail Make it pass! Commit/Push
  200. antisocial network Let’s do it! Create appschema test Use pgTAP

    Run test with pg_prove Make it fail Make it pass! Commit/Push https:/ /github.com/ theory/agile-flipr.git
  201. Why TDDD Ensure data quality Data is a core asset

    Validate business rules antisocial network
  202. Why TDDD Ensure data quality Data is a core asset

    Validate business rules Stored procedures antisocial network
  203. Why TDDD Ensure data quality Data is a core asset

    Validate business rules Stored procedures Triggers antisocial network
  204. Why TDDD Ensure data quality Data is a core asset

    Validate business rules Stored procedures Triggers Views antisocial network
  205. Why TDDD Identify defects early …and often! Create design iteratively

    Evolutionarily Validate refactorings antisocial network
  206. Why TDDD Identify defects early …and often! Create design iteratively

    Evolutionarily Validate refactorings Make sure nothing breaks antisocial network
  207. “If you’re implementing code in the DB in the form

    of stored procedures, triggers... shouldn’t you test that code to the same level that you test your app code?”
  208. “Think of all the data quality problems you’ve run into

    over the years. Wouldn't it have been nice if someone had originally tested and discovered those problems before you did?”
  209. “Wouldn’t it be nice to have a test suite to

    run so that you could determine how (and if) the DB actually works?”
  210. TDD How Ideally separate from app tests May be many

    apps DB should present interface to all antisocial network
  211. TDD How Ideally separate from app tests May be many

    apps DB should present interface to all Apps may use different permissions antisocial network
  212. TDD How Ideally separate from app tests May be many

    apps DB should present interface to all Apps may use different permissions Ideally use DB test Framework antisocial network
  213. TDD How Ideally separate from app tests May be many

    apps DB should present interface to all Apps may use different permissions Ideally use DB test Framework Like…pgTAP! antisocial network
  214. Test Template Sqitch scripts generated from templates Can add new

    templates for new files Always want test for change?
  215. Test Template Sqitch scripts generated from templates Can add new

    templates for new files Always want test for change? Add test template!
  216. Templates Where?    mkdir  -­‐p  ~/.sqitch/templates/test > >    cp

     test/appschema.sql  ~/.sqitch/templates/test/pg.tmpl > Will create file in project test directory.
  217. pg.tmpl Template Your Tests SET  client_min_messages  TO  warning;   CREATE

     EXTENSION  IF  NOT  EXISTS  pgtap;   RESET  client_min_messages;   ! BEGIN;   ! ! ! SELECT   ! SELECT  finish();   ROLLBACK; has_schema('flipr'); SELECT  plan(1);  
  218. pg.tmpl Template Your Tests SET  client_min_messages  TO  warning;   CREATE

     EXTENSION  IF  NOT  EXISTS  pgtap;   RESET  client_min_messages;   ! BEGIN;   ! ! ! SELECT   ! SELECT  finish();   ROLLBACK; has_schema('flipr'); Adjust test counts. SELECT  no_plan(); -­‐-­‐  SELECT  plan(1);
  219. pg.tmpl Template Your Tests SET  client_min_messages  TO  warning;   CREATE

     EXTENSION  IF  NOT  EXISTS  pgtap;   RESET  client_min_messages;   ! BEGIN;   ! ! ! SELECT   ! SELECT  finish();   ROLLBACK; pass('Test  [%  change  %]!'); Put tests here. SELECT  no_plan(); -­‐-­‐  SELECT  plan(1);
  220.    git  checkout  -­‐b  users  master Switched  to  a  new

     branch  'users' > Branching Out > Branched off from others
  221.    git  checkout  -­‐b  users  master Switched  to  a  new

     branch  'users' > Branching Out > >  sqitch  add  users  -­‐-­‐requires  appschema  \    -­‐n  'Creates  table  to  track  our  users.' Created  deploy/users.sql Created  revert/users.sql Created  test/users.sql Created  verify/users.sql Added  "users  [appschema]"  to  sqitch.plan >
  222.    git  checkout  -­‐b  users  master Switched  to  a  new

     branch  'users' > Branching Out > >  sqitch  add  users  -­‐-­‐requires  appschema  \    -­‐n  'Creates  table  to  track  our  users.' Created  deploy/users.sql Created  revert/users.sql Created  test/users.sql Created  verify/users.sql Added  "users  [appschema]"  to  sqitch.plan > Cool!
  223.    git  checkout  -­‐b  users  master Switched  to  a  new

     branch  'users' > Branching Out > >  sqitch  add  users  -­‐-­‐requires  appschema  \    -­‐n  'Creates  table  to  track  our  users.' Created  deploy/users.sql Created  revert/users.sql Created  test/users.sql Created  verify/users.sql Added  "users  [appschema]"  to  sqitch.plan >
  224.    git  checkout  -­‐b  users  master Switched  to  a  new

     branch  'users' > Branching Out > >  emacs  test/users.sql > >  sqitch  add  users  -­‐-­‐requires  appschema  \    -­‐n  'Creates  table  to  track  our  users.' Created  deploy/users.sql Created  revert/users.sql Created  test/users.sql Created  verify/users.sql Added  "users  [appschema]"  to  sqitch.plan >
  225. SET  client_min_messages  TO  warning;   CREATE  EXTENSION  IF  NOT  EXISTS

     pgtap;   RESET  client_min_messages;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! ! ! ! test/users.sql Table For One SELECT  pass('Test  users!'); SELECT  finish();   ROLLBACK;
  226. SET  client_min_messages  TO  warning;   CREATE  EXTENSION  IF  NOT  EXISTS

     pgtap;   RESET  client_min_messages;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! ! ! ! test/users.sql Table For One SELECT  pass('Test  users!'); SELECT  finish();   ROLLBACK;
  227. SET  client_min_messages  TO  warning;   CREATE  EXTENSION  IF  NOT  EXISTS

     pgtap;   RESET  client_min_messages;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! ! ! ! test/users.sql Table For One SET  search_path  TO  flipr,public; SELECT  has_table(  'users'  ); SELECT  finish();   ROLLBACK;
  228. SET  client_min_messages  TO  warning;   CREATE  EXTENSION  IF  NOT  EXISTS

     pgtap;   RESET  client_min_messages;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! ! ! ! test/users.sql Table For One SET  search_path  TO  flipr,public; SELECT  has_table(  'users'  ); SELECT  finish();   ROLLBACK;
  229. SET  client_min_messages  TO  warning;   CREATE  EXTENSION  IF  NOT  EXISTS

     pgtap;   RESET  client_min_messages;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! ! ! ! test/users.sql Table For One SET  search_path  TO  flipr,public; SELECT  has_table(  'users'  ); SELECT  finish();   ROLLBACK;
  230. pg_prove -­‐d flipr_test -­‐v test/users.sql test/users.sql .. not ok 1

    -­‐ Table users should exist # Failed test 1: "Table users should exist" 1..1 # Looks like you failed 1 test of 1 Failed 1/1 subtests Test Summary Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/users.sql (Wstat: 0 Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL > Run ’Em >
  231. pg_prove -­‐d flipr_test -­‐v test/users.sql test/users.sql .. not ok 1

    -­‐ Table users should exist # Failed test 1: "Table users should exist" 1..1 # Looks like you failed 1 test of 1 Failed 1/1 subtests Test Summary Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/users.sql (Wstat: 0 Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL > Run ’Em >
  232. pg_prove -­‐d flipr_test -­‐v test/users.sql test/users.sql .. not ok 1

    -­‐ Table users should exist # Failed test 1: "Table users should exist" 1..1 # Looks like you failed 1 test of 1 Failed 1/1 subtests Test Summary Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/users.sql (Wstat: 0 Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL > Run ’Em As expected. >
  233. pg_prove -­‐d flipr_test -­‐v test/users.sql test/users.sql .. not ok 1

    -­‐ Table users should exist # Failed test 1: "Table users should exist" 1..1 # Looks like you failed 1 test of 1 Failed 1/1 subtests Test Summary Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/users.sql (Wstat: 0 Tests: 1 Failed: 1) Failed test: 1 Files=1, Tests=1, 0 wallclock secs Result: FAIL > Run ’Em >    emacs  deploy/users.sql
  234. deploy/users.sq deploy/users.sql -­‐-­‐  XXX  Add  DDLs  here. COMMIT; -­‐-­‐  Deploy

     users   -­‐-­‐  requires:  appschema   ! BEGIN;   ! 

  235. deploy/users.sq deploy/users.sql -­‐-­‐  XXX  Add  DDLs  here. COMMIT; -­‐-­‐  Deploy

     users   -­‐-­‐  requires:  appschema   ! BEGIN;   ! 

  236. SET  client_min_messages  =  'warning'; CREATE  TABLE  flipr.users  (    

       nickname    TEXT ); deploy/users.sq deploy/users.sql COMMIT; -­‐-­‐  Deploy  users   -­‐-­‐  requires:  appschema   ! BEGIN;   ! 

  237. SET  client_min_messages  =  'warning'; CREATE  TABLE  flipr.users  (    

       nickname    TEXT ); deploy/users.sq deploy/users.sql COMMIT; -­‐-­‐  Deploy  users   -­‐-­‐  requires:  appschema   ! BEGIN;   ! 
 Bare Minimum
  238. -­‐-­‐  Verify  users   ! BEGIN; SELECT  nickname    FROM

     flipr.users  WHERE  FALSE; verify/users.sq verify/users.sql ROLLBACK;
  239. revert/users.sq revert/users.sql -­‐-­‐  Revert  users   ! BEGIN;   !

    ! ! COMMIT; -­‐-­‐  XXX  Add  DDLs  here.
  240.    sqitch  deploy Deploying  changes  to  flipr_test    +  users

     ..  ok > Make Users >    psql  -­‐d  flipr_test  -­‐c  '\d  flipr.users'          Table  "flipr.users"    Column    |  Type  |  Modifiers   -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  nickname  |  text  |   >
  241.    sqitch  deploy Deploying  changes  to  flipr_test    +  users

     ..  ok > Make Users >    psql  -­‐d  flipr_test  -­‐c  '\d  flipr.users'          Table  "flipr.users"    Column    |  Type  |  Modifiers   -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  nickname  |  text  |   >    sqitch  verify Verifying  flipr_test    *  appschema  ..  ok    *  users  ......  ok Verify  successful >
  242.    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok

     1  -­‐  Table  users  should  exist 1..1 ok All  tests  successful. Files=1,  Tests=1,    1  wallclock  secs Result:  PASS > Make Users >
  243.    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok

     1  -­‐  Table  users  should  exist 1..1 ok All  tests  successful. Files=1,  Tests=1,    1  wallclock  secs Result:  PASS > Make Users >
  244.    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok

     1  -­‐  Table  users  should  exist 1..1 ok All  tests  successful. Files=1,  Tests=1,    1  wallclock  secs Result:  PASS > Make Users > Woohoo!
  245.    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok

     1  -­‐  Table  users  should  exist 1..1 ok All  tests  successful. Files=1,  Tests=1,    1  wallclock  secs Result:  PASS > Make Users >    emacs  test/users.sql >
  246. SET  client_min_messages  TO  warning;   CREATE  EXTENSION  IF  NOT  EXISTS

     pgtap;   RESET  client_min_messages;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! SET  search_path  TO  flipr,public;   SELECT  has_table(  'users'  ); test/users.sql Columnist SELECT  finish();   ROLLBACK;
  247. SET  client_min_messages  TO  warning;   CREATE  EXTENSION  IF  NOT  EXISTS

     pgtap;   RESET  client_min_messages;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! SET  search_path  TO  flipr,public;   SELECT  has_table(  'users'  ); SELECT  has_column(  'users',  'nickname'    ); SELECT  has_column(  'users',  'password'    ); SELECT  has_column(  'users',  'timestamp'  ); test/users.sql Columnist SELECT  finish();   ROLLBACK;
  248.    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok

     1  -­‐  Table  users  should  exist ok  2  -­‐  Column  users.nickname  should  exist not  ok  3  -­‐  Column  users.password  should  exist #  Failed  test  3:  "Column  users.password  should  exist" not  ok  4  -­‐  Column  users."timestamp"  should  exist #  Failed  test  4:  "Column  users."timestamp"  should  exist" 1..4 #  Looks  like  you  failed  2  tests  of  4 Failed  2/4  subtests   Test  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/users.sql  (Wstat:  0  Tests:  4  Failed:  2)    Failed  tests:    3-­‐4 Files=1,  Tests=4,    0  wallclock  secs Result:  FAIL Dead Again >
  249.    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok

     1  -­‐  Table  users  should  exist ok  2  -­‐  Column  users.nickname  should  exist not  ok  3  -­‐  Column  users.password  should  exist #  Failed  test  3:  "Column  users.password  should  exist" not  ok  4  -­‐  Column  users."timestamp"  should  exist #  Failed  test  4:  "Column  users."timestamp"  should  exist" 1..4 #  Looks  like  you  failed  2  tests  of  4 Failed  2/4  subtests   Test  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/users.sql  (Wstat:  0  Tests:  4  Failed:  2)    Failed  tests:    3-­‐4 Files=1,  Tests=4,    0  wallclock  secs Result:  FAIL Dead Again > Guess we should add them.
  250. -­‐-­‐  Deploy  users   -­‐-­‐  requires:  appschema   ! BEGIN;

      ! SET  client_min_messages  =  'warning';   CREATE  TABLE  flipr.users  (          nickname    TEXT deploy/users.sq deploy/users.sql );   ! COMMIT;
  251. -­‐-­‐  Deploy  users   -­‐-­‐  requires:  appschema   ! BEGIN;

      ! SET  client_min_messages  =  'warning';   CREATE  TABLE  flipr.users  (          nickname    TEXT                                    ,        password    TEXT,        timestamp  TIMESTAMPTZ deploy/users.sq deploy/users.sql );   ! COMMIT;
  252. -­‐-­‐  Verify  users   ! BEGIN;   ! SELECT  nickname

         FROM  flipr.users    WHERE  FALSE;   ! COMMIT; verify/users.sq verify/users.sql
  253. -­‐-­‐  Verify  users   ! BEGIN;   ! SELECT  nickname

         FROM  flipr.users    WHERE  FALSE;   ! COMMIT; ,  password,  timestamp verify/users.sq verify/users.sql
  254. Revert Overhead    sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y Reverting  changes

     to  appschema  from  flipr_test    -­‐  users  ..  ok > >  emacs  deploy/users.sql   >  emacs  verify/users.sql   >
  255. Revert Overhead    sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y Reverting  changes

     to  appschema  from  flipr_test    -­‐  users  ..  ok > Yes, really. >  emacs  deploy/users.sql   >  emacs  verify/users.sql   >
  256. Revert Overhead    sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y Reverting  changes

     to  appschema  from  flipr_test    -­‐  users  ..  ok > >  emacs  deploy/users.sql   >  emacs  verify/users.sql   >
  257. Revert Overhead    sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y Reverting  changes

     to  appschema  from  flipr_test    -­‐  users  ..  ok > Remove >  emacs  deploy/users.sql   >  emacs  verify/users.sql   >
  258. Revert Overhead    sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y Reverting  changes

     to  appschema  from  flipr_test    -­‐  users  ..  ok > >  emacs  deploy/users.sql   >  emacs  verify/users.sql   > What’s that?
  259. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change antisocial network
  260. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change @ROOT First change antisocial network
  261. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change @ROOT First change Two modifiers: antisocial network
  262. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change @ROOT First change Two modifiers: ^ Previous change antisocial network
  263. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change @ROOT First change Two modifiers: ^ Previous change ~ Following change antisocial network
  264. Specifying Changes users Change named “users” @HEAD^ Second to last

    change users^ Change before users antisocial network
  265. Specifying Changes users Change named “users” @HEAD^ Second to last

    change users^ Change before users @ROOT~ Second change antisocial network
  266. Specifying Changes users Change named “users” @HEAD^ Second to last

    change users^ Change before users @ROOT~ Second change appschema~ Change after appschema antisocial network
  267. Specifying Changes users Change named “users” @HEAD^ Second to last

    change users^ Change before users @ROOT~ Second change appschema~ Change after appschema @HEAD^^ Third to last change antisocial network
  268. Specifying Changes users Change named “users” @HEAD^ Second to last

    change users^ Change before users @ROOT~ Second change appschema~ Change after appschema @HEAD^^ Third to last change users~4 4th change after antisocial network
  269. >    sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y   Reverting  changes

     to  appschema  from  flipr_test      -­‐  users  ..  ok   > Whither Users >
  270. >    sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y   Reverting  changes

     to  appschema  from  flipr_test      -­‐  users  ..  ok   > Whither Users > >  psql  -­‐d  flipr_test  -­‐c  '\d  flipr.users' Did  not  find  any  relation  named  "flipr.users". >
  271. >    sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y   Reverting  changes

     to  appschema  from  flipr_test      -­‐  users  ..  ok   >    sqitch  status #  On  database  flipr_test #  Project:    flipr #  Change:      e01feba4bb79607339a13981574855b6e2aa7526 #  Name:          appschema #  Deployed:  2014-­‐01-­‐07  16:13:50  -­‐0800 #  By:              David  E.  Wheeler  <[email protected]> #   Undeployed  change:    *  users Whither Users > >  psql  -­‐d  flipr_test  -­‐c  '\d  flipr.users' Did  not  find  any  relation  named  "flipr.users". >
  272. >  sqitch  verify Verifying  flipr_test    *  appschema  ..  ok

    Undeployed  change:    *  users Verify  successful > Whither Users >
  273.    sqitch  deploy Deploying  changes  to  flipr_test    +  users

     ..  ok > Back At It >    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok  1  -­‐  Table  users  should  exist ok  2  -­‐  Column  users.nickname  should  exist ok  3  -­‐  Column  users.password  should  exist ok  4  -­‐  Column  users."timestamp"  should  exist 1..4 ok All  tests  successful. Files=1,  Tests=4,    0  wallclock  secs Result:  PASS >
  274.    sqitch  deploy Deploying  changes  to  flipr_test    +  users

     ..  ok > Back At It >    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok  1  -­‐  Table  users  should  exist ok  2  -­‐  Column  users.nickname  should  exist ok  3  -­‐  Column  users.password  should  exist ok  4  -­‐  Column  users."timestamp"  should  exist 1..4 ok All  tests  successful. Files=1,  Tests=4,    0  wallclock  secs Result:  PASS > Woot!
  275.    sqitch  deploy Deploying  changes  to  flipr_test    +  users

     ..  ok > Back At It >    pg_prove  -­‐d  flipr_test  -­‐v  test/users.sql test/users.sql  ..   ok  1  -­‐  Table  users  should  exist ok  2  -­‐  Column  users.nickname  should  exist ok  3  -­‐  Column  users.password  should  exist ok  4  -­‐  Column  users."timestamp"  should  exist 1..4 ok All  tests  successful. Files=1,  Tests=4,    0  wallclock  secs Result:  PASS >    emacs  test/users.sql
  276. SET  search_path  =  public,tap;   ! BEGIN;   SELECT  no_plan();

      -­‐-­‐  SELECT  plan(1);   ! SELECT  has_table(    'users'  ); SELECT  has_column(                'users',  'timestamp'  );   test/users.sql SELECT  has_column(                'users',  'nickname'  ); SELECT  has_column(                'users',  'password'  ); SELECT  finish();   ROLLBACK;
  277. SET  search_path  =  public,tap;   ! BEGIN;   SELECT  no_plan();

      -­‐-­‐  SELECT  plan(1);   ! SELECT  has_table(    'users'  ); SELECT  has_column(                'users',  'timestamp'  );   test/users.sql SELECT  has_column(                'users',  'nickname'  ); SELECT  has_column(                'users',  'password'  ); SELECT  has_pk(          'users'  ); SELECT  finish();   ROLLBACK;
  278. SELECT  col_type_is(              'users',  'nickname',

     'text'  ); SELECT  col_hasnt_default(  'users',  'nickname'  ); SELECT  col_is_pk(                  'users',  'nickname'  ); SET  search_path  =  public,tap;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! SELECT  has_table(    'users'  ); SELECT  has_column(                'users',  'timestamp'  );   test/users.sql SELECT  has_column(                'users',  'nickname'  ); SELECT  has_column(                'users',  'password'  ); SELECT  has_pk(          'users'  ); SELECT  finish();   ROLLBACK;
  279. SELECT  col_type_is(              'users',  'nickname',

     'text'  ); SELECT  col_hasnt_default(  'users',  'nickname'  ); SELECT  col_is_pk(                  'users',  'nickname'  ); SET  search_path  =  public,tap;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! SELECT  has_table(    'users'  ); SELECT  has_column(                'users',  'timestamp'  );   test/users.sql SELECT  has_column(                'users',  'nickname'  ); SELECT  has_column(                'users',  'password'  ); SELECT  has_pk(          'users'  ); SELECT  col_type_is(              'users',  'password',  'text'  ); SELECT  col_not_null(            'users',  'password'  ); SELECT  col_hasnt_default(  'users',  'password'  ); SELECT  finish();   ROLLBACK;
  280. SELECT  col_type_is(              'users',  'nickname',

     'text'  ); SELECT  col_hasnt_default(  'users',  'nickname'  ); SELECT  col_is_pk(                  'users',  'nickname'  ); SET  search_path  =  public,tap;   ! BEGIN;   SELECT  no_plan();   -­‐-­‐  SELECT  plan(1);   ! SELECT  has_table(    'users'  ); SELECT  has_column(                'users',  'timestamp'  );   test/users.sql SELECT  has_column(                'users',  'nickname'  ); SELECT  has_column(                'users',  'password'  ); SELECT  has_pk(          'users'  ); SELECT  col_type_is('users',  'timestamp',  'timestamp  with  time  zone'); SELECT  col_not_null(            'users',  'timestamp'  ); SELECT  col_has_default(      'users',  'timestamp'  ); SELECT  col_default_is(        'users',  'timestamp',  'now()'  ); SELECT  col_type_is(              'users',  'password',  'text'  ); SELECT  col_not_null(            'users',  'password'  ); SELECT  col_hasnt_default(  'users',  'password'  ); SELECT  finish();   ROLLBACK;
  281. >  pg_prove  -­‐d  flipr_test  test/users.sql test/users.sql  ..  1/?   #

     Failed  test  2:  "Table  users  should  have  a  primary  key" #  Failed  test  6:  "Column  users(nickname)  should  be  a  primary  key" #                  have:  NULL #                  want:  {nickname} #  Failed  test  9:  "Column  users.password  should  be  NOT  NULL" #  Failed  test  13:  "Column  users."timestamp"  should  be  NOT  NULL" #  Failed  test  14:  "Column  users."timestamp"  should  have  a  default" #  Failed  test  15:  "Column  users."timestamp"  should  default  to  'now()'" #          Column  users."timestamp"  has  no  default #  Looks  like  you  failed  6  tests  of  15 test/users.sql  ..  Failed  6/15  subtests   Test  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/users.sql  (Wstat:  0  Tests:  15  Failed:  6)    Failed  tests:    2,  6,  9,  13-­‐15 Files=1,  Tests=15,    0  wallclock  secs Result:  FAIL Columny >
  282. >  pg_prove  -­‐d  flipr_test  test/users.sql test/users.sql  ..  1/?   #

     Failed  test  2:  "Table  users  should  have  a  primary  key" #  Failed  test  6:  "Column  users(nickname)  should  be  a  primary  key" #                  have:  NULL #                  want:  {nickname} #  Failed  test  9:  "Column  users.password  should  be  NOT  NULL" #  Failed  test  13:  "Column  users."timestamp"  should  be  NOT  NULL" #  Failed  test  14:  "Column  users."timestamp"  should  have  a  default" #  Failed  test  15:  "Column  users."timestamp"  should  default  to  'now()'" #          Column  users."timestamp"  has  no  default #  Looks  like  you  failed  6  tests  of  15 test/users.sql  ..  Failed  6/15  subtests   Test  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/users.sql  (Wstat:  0  Tests:  15  Failed:  6)    Failed  tests:    2,  6,  9,  13-­‐15 Files=1,  Tests=15,    0  wallclock  secs Result:  FAIL Columny > Let’s make it so.
  283. -­‐-­‐  Deploy  users   -­‐-­‐  requires:  appschema   ! BEGIN;

      ! SET  client_min_messages  =  'warning';   CREATE  TABLE  flipr.users  (          nickname    TEXT          password    TEXT          timestamp  TIMESTAMPTZ   );   ! COMMIT; deploy/users.sq deploy/users.sql , ,
  284. -­‐-­‐  Deploy  users   -­‐-­‐  requires:  appschema   ! BEGIN;

      ! SET  client_min_messages  =  'warning';   CREATE  TABLE  flipr.users  (          nickname    TEXT          password    TEXT          timestamp  TIMESTAMPTZ   );   ! COMMIT; deploy/users.sq deploy/users.sql ,                PRIMARY  KEY,
  285. -­‐-­‐  Deploy  users   -­‐-­‐  requires:  appschema   ! BEGIN;

      ! SET  client_min_messages  =  'warning';   CREATE  TABLE  flipr.users  (          nickname    TEXT          password    TEXT          timestamp  TIMESTAMPTZ   );   ! COMMIT; deploy/users.sq deploy/users.sql                PRIMARY  KEY,                NOT  NULL,
  286. -­‐-­‐  Deploy  users   -­‐-­‐  requires:  appschema   ! BEGIN;

      ! SET  client_min_messages  =  'warning';   CREATE  TABLE  flipr.users  (          nickname    TEXT          password    TEXT          timestamp  TIMESTAMPTZ   );   ! COMMIT; deploy/users.sq deploy/users.sql                PRIMARY  KEY,                NOT  NULL,  NOT  NULL  DEFAULT  NOW()
  287.    sqitch  rebase  @HEAD^  -­‐y Reverting  changes  to  appschema  from

     flipr_test    -­‐  users  ..  ok Deploying  changes  to  flipr_test    +  users  ..  ok > User Typography >  emacs  deploy/users.sql   >
  288.    sqitch  rebase  @HEAD^  -­‐y Reverting  changes  to  appschema  from

     flipr_test    -­‐  users  ..  ok Deploying  changes  to  flipr_test    +  users  ..  ok > User Typography >  emacs  deploy/users.sql   >
  289.    sqitch  rebase  @HEAD^  -­‐y Reverting  changes  to  appschema  from

     flipr_test    -­‐  users  ..  ok Deploying  changes  to  flipr_test    +  users  ..  ok > User Typography >  emacs  deploy/users.sql   >
  290.    sqitch  rebase  @HEAD^  -­‐y Reverting  changes  to  appschema  from

     flipr_test    -­‐  users  ..  ok Deploying  changes  to  flipr_test    +  users  ..  ok > User Typography >  emacs  deploy/users.sql   >    pg_prove  -­‐d  flipr_test  test/users.sql test/users.sql  ..  ok         All  tests  successful. Files=1,  Tests=15,    0  wallclock  secs Result:  PASS >
  291.    sqitch  rebase  @HEAD^  -­‐y Reverting  changes  to  appschema  from

     flipr_test    -­‐  users  ..  ok Deploying  changes  to  flipr_test    +  users  ..  ok > User Typography >  emacs  deploy/users.sql   >    pg_prove  -­‐d  flipr_test  test/users.sql test/users.sql  ..  ok         All  tests  successful. Files=1,  Tests=15,    0  wallclock  secs Result:  PASS > Boom.
  292. Additives >    git  add  . >    git  commit

     -­‐am  'Add  users  table.' [users  693ca89]  Add  users  table.  5  files  changed,  60  insertions(+)  create  mode  100644  deploy/users.sql  create  mode  100644  revert/users.sql  create  mode  100644  test/users.sql  create  mode  100644  verify/users.sql >
  293. Pushers >    git  push  -­‐-­‐set-­‐upstream  origin  users Counting  objects:

     20,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (14/14),  done. Writing  objects:  100%  (15/15),  1.95  KiB,  done. Total  15  (delta  2),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git  *  [new  branch]            users  -­‐>  users Branch  users  set  up  to  track  remote  branch  users   from  origin. >
  294. Pushers >    git  push  -­‐-­‐set-­‐upstream  origin  users Counting  objects:

     20,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (14/14),  done. Writing  objects:  100%  (15/15),  1.95  KiB,  done. Total  15  (delta  2),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git  *  [new  branch]            users  -­‐>  users Branch  users  set  up  to  track  remote  branch  users   from  origin. >
  295. Pushers >    git  push  -­‐-­‐set-­‐upstream  origin  users Counting  objects:

     20,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (14/14),  done. Writing  objects:  100%  (15/15),  1.95  KiB,  done. Total  15  (delta  2),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git  *  [new  branch]            users  -­‐>  users Branch  users  set  up  to  track  remote  branch  users   from  origin. >
  296. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change Revise test antisocial network
  297. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change Revise test Revise and rebase change antisocial network
  298. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change Revise test Revise and rebase change Wash, Rinse, Repeat antisocial network
  299. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change Revise test Revise and rebase change Wash, Rinse, Repeat Commit/Push when done antisocial network
  300. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change Revise test Revise and rebase change Wash, Rinse, Repeat Commit/Push when done Breathe in, breathe out antisocial network
  301. Time to Work! Prepare to hack! git checkout master git

    branch -D users antisocial network
  302. Time to Work! Prepare to hack! git checkout master git

    branch -D users git checkout -b users antisocial network
  303. Time to Work! Prepare to hack! git checkout master git

    branch -D users git checkout -b users git reset --hard upstream/users antisocial network
  304. Time to Work! Prepare to hack! git checkout master git

    branch -D users git checkout -b users git reset --hard upstream/users git log antisocial network
  305. Time to Work! Prepare to hack! git checkout master git

    branch -D users git checkout -b users git reset --hard upstream/users git log Should be at “Add users table.” antisocial network
  306. Caution: Hard Reset Ahead A rare destructive Git command Deletes

    HEAD snapshot Replaces it with new snapshot
  307. Caution: Hard Reset Ahead A rare destructive Git command Deletes

    HEAD snapshot Replaces it with new snapshot Almost un-reversible
  308. Caution: Hard Reset Ahead A rare destructive Git command Deletes

    HEAD snapshot Replaces it with new snapshot Almost un-reversible Useful for starting from known point
  309. Caution: Hard Reset Ahead A rare destructive Git command Deletes

    HEAD snapshot Replaces it with new snapshot Almost un-reversible Useful for starting from known point USE WITH CAUTION!
  310. antisocial network Flip Out Create flips branch Create flips table

    flip_id SERIAL PK nickname FK body TEXT timestamptz
  311. antisocial network Flip Out Create flips branch Create flips table

    flip_id SERIAL PK nickname FK body TEXT timestamptz Use TDDD
  312. antisocial network Flip Out Create flips branch Create flips table

    flip_id SERIAL PK nickname FK body TEXT timestamptz Use TDDD https:/ /github.com/ theory/agile-flipr.git
  313. Functional Testing    git  checkout  -­‐b  userfuncs  users Switched  to

     a  new  branch  'userfuncs' > > Branches from users
  314. Functional Testing    git  checkout  -­‐b  userfuncs  users Switched  to

     a  new  branch  'userfuncs' > >    sqitch  add  insert_user  -­‐r  users  -­‐r  appschema  \            -­‐n  'Creates  a  function  to  insert  a  user.' Created  deploy/insert_user.sql Created  revert/insert_user.sql Created  test/insert_user.sql Created  verify/insert_user.sql Added  "insert_user  [users  appschema]"  to  sqitch.plan >
  315. Functional Testing    git  checkout  -­‐b  userfuncs  users Switched  to

     a  new  branch  'userfuncs' > >    sqitch  add  insert_user  -­‐r  users  -­‐r  appschema  \            -­‐n  'Creates  a  function  to  insert  a  user.' Created  deploy/insert_user.sql Created  revert/insert_user.sql Created  test/insert_user.sql Created  verify/insert_user.sql Added  "insert_user  [users  appschema]"  to  sqitch.plan >    emacs  test/insert_user.sql >
  316. test/insert_use test/insert_user.sql SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF  NOT

     EXISTS  pgtap; RESET  client_min_messages; SET  search_path  TO  flipr,public;
  317. test/insert_use test/insert_user.sql SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF  NOT

     EXISTS  pgtap; RESET  client_min_messages; SET  search_path  TO  flipr,public; BEGIN; -­‐-­‐  SELECT  no_plan(); SELECT  plan(11);
  318. test/insert_use test/insert_user.sql SET  client_min_messages  TO  warning; CREATE  EXTENSION  IF  NOT

     EXISTS  pgtap; RESET  client_min_messages; SET  search_path  TO  flipr,public; BEGIN; -­‐-­‐  SELECT  no_plan(); SELECT  plan(11);
  319. test/insert_use SELECT  plan(11); SELECT  has_function(  'insert_user'  ); SELECT  has_function(  

         'insert_user',  ARRAY['text',  'text'] ); SELECT  function_lang_is(        'insert_user',  ARRAY['text',  'text'],        'sql' ); SELECT  function_returns(        'insert_user',  ARRAY['text',  'text'],        'void' ); SELECT  volatility_is(        'insert_user',  ARRAY['text',  'text'],        'volatile' );
  320. test/insert_use SELECT  plan(11); SELECT  has_function(  'insert_user'  ); SELECT  has_function(  

         'insert_user',  ARRAY['text',  'text'] ); SELECT  function_lang_is(        'insert_user',  ARRAY['text',  'text'],        'sql' ); SELECT  function_returns(        'insert_user',  ARRAY['text',  'text'],        'void' ); SELECT  volatility_is(        'insert_user',  ARRAY['text',  'text'],        'volatile' );
  321. test/insert_use SELECT  plan(11); SELECT  has_function(  'insert_user'  ); SELECT  has_function(  

         'insert_user',  ARRAY['text',  'text'] ); SELECT  function_lang_is(        'insert_user',  ARRAY['text',  'text'],        'sql' ); SELECT  function_returns(        'insert_user',  ARRAY['text',  'text'],        'void' ); SELECT  volatility_is(        'insert_user',  ARRAY['text',  'text'],        'volatile' );
  322. test/insert_use SELECT  plan(11); SELECT  has_function(  'insert_user'  ); SELECT  has_function(  

         'insert_user',  ARRAY['text',  'text'] ); SELECT  function_lang_is(        'insert_user',  ARRAY['text',  'text'],        'sql' ); SELECT  function_returns(        'insert_user',  ARRAY['text',  'text'],        'void' ); SELECT  volatility_is(        'insert_user',  ARRAY['text',  'text'],        'volatile' );
  323. test/insert_use SELECT  plan(11); SELECT  has_function(  'insert_user'  ); SELECT  has_function(  

         'insert_user',  ARRAY['text',  'text'] ); SELECT  function_lang_is(        'insert_user',  ARRAY['text',  'text'],        'sql' ); SELECT  function_returns(        'insert_user',  ARRAY['text',  'text'],        'void' ); SELECT  volatility_is(        'insert_user',  ARRAY['text',  'text'],        'volatile' );
  324. test/insert_use test/insert_user.sql        'volatile'   ); SELECT  lives_ok(

           $$  SELECT  insert_user('theory',  'foo')  $$,        'Insert  a  user' ); SELECT  row_eq(      'SELECT  *  FROM  users',      ROW('theory',  md5('foo'),  NOW())::users,      'The  user  should  have  been  inserted' );
  325. test/insert_use test/insert_user.sql        'volatile'   ); SELECT  lives_ok(

           $$  SELECT  insert_user('theory',  'foo')  $$,        'Insert  a  user' ); SELECT  row_eq(      'SELECT  *  FROM  users',      ROW('theory',  md5('foo'),  NOW())::users,      'The  user  should  have  been  inserted' );
  326. test/insert_use test/insert_user.sql        'volatile'   ); SELECT  lives_ok(

           $$  SELECT  insert_user('theory',  'foo')  $$,        'Insert  a  user' ); SELECT  row_eq(      'SELECT  *  FROM  users',      ROW('theory',  md5('foo'),  NOW())::users,      'The  user  should  have  been  inserted' );
  327. test/insert_use test/insert_user.sql        'volatile'   ); SELECT  lives_ok(

           $$  SELECT  insert_user('theory',  'foo')  $$,        'Insert  a  user' ); SELECT  row_eq(      'SELECT  *  FROM  users',      ROW('theory',  md5('foo'),  NOW())::users,      'The  user  should  have  been  inserted' );
  328. test/insert_use test/insert_user.sql        'volatile'   ); SELECT  lives_ok(

           $$  SELECT  insert_user('theory',  'foo')  $$,        'Insert  a  user' ); SELECT  row_eq(      'SELECT  *  FROM  users',      ROW('theory',  md5('foo'),  NOW())::users,      'The  user  should  have  been  inserted' );
  329. test/insert_use test/insert_user.sql      'The  user  should  have  been  inserted'

      ); SELECT  lives_ok(        $$  SELECT  insert_user('strongrrl',  'w00t')  $$,        'Insert  another  user' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Both  users  should  be  present' );
  330. test/insert_use test/insert_user.sql      'The  user  should  have  been  inserted'

      ); SELECT  lives_ok(        $$  SELECT  insert_user('strongrrl',  'w00t')  $$,        'Insert  another  user' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Both  users  should  be  present' );
  331. test/insert_use test/insert_user.sql      'The  user  should  have  been  inserted'

      ); SELECT  lives_ok(        $$  SELECT  insert_user('strongrrl',  'w00t')  $$,        'Insert  another  user' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Both  users  should  be  present' ); Includes dupes
  332. test/insert_use test/insert_user.sql      'The  user  should  have  been  inserted'

      ); SELECT  lives_ok(        $$  SELECT  insert_user('strongrrl',  'w00t')  $$,        'Insert  another  user' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Both  users  should  be  present' );
  333. test/insert_use test/insert_user.sql      'The  user  should  have  been  inserted'

      ); SELECT  lives_ok(        $$  SELECT  insert_user('strongrrl',  'w00t')  $$,        'Insert  another  user' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Both  users  should  be  present' );
  334. SELECT  throws_ok(        $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,

           23505,  -­‐-­‐  duplicate  key  violation        NULL,    -­‐-­‐  localized  error  message        'Should  get  an  error  for  duplicate  nickname' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Should  still  have  just  the  two  users' ); test/insert_use        'Both  users  should  be  present'   );
  335. SELECT  throws_ok(        $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,

           23505,  -­‐-­‐  duplicate  key  violation        NULL,    -­‐-­‐  localized  error  message        'Should  get  an  error  for  duplicate  nickname' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Should  still  have  just  the  two  users' ); test/insert_use        'Both  users  should  be  present'   );
  336. SELECT  throws_ok(        $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,

           23505,  -­‐-­‐  duplicate  key  violation        NULL,    -­‐-­‐  localized  error  message        'Should  get  an  error  for  duplicate  nickname' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Should  still  have  just  the  two  users' ); test/insert_use        'Both  users  should  be  present'   ); Appendix A
  337. SELECT  throws_ok(        $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,

           23505,  -­‐-­‐  duplicate  key  violation        NULL,    -­‐-­‐  localized  error  message        'Should  get  an  error  for  duplicate  nickname' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Should  still  have  just  the  two  users' ); test/insert_use        'Both  users  should  be  present'   );
  338. SELECT  throws_ok(        $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,

           23505,  -­‐-­‐  duplicate  key  violation        NULL,    -­‐-­‐  localized  error  message        'Should  get  an  error  for  duplicate  nickname' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Should  still  have  just  the  two  users' ); test/insert_use        'Both  users  should  be  present'   );
  339. SELECT  throws_ok(        $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,

           23505,  -­‐-­‐  duplicate  key  violation        NULL,    -­‐-­‐  localized  error  message        'Should  get  an  error  for  duplicate  nickname' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Should  still  have  just  the  two  users' ); test/insert_use        'Both  users  should  be  present'   );
  340. SELECT  throws_ok(        $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,

           23505,  -­‐-­‐  duplicate  key  violation        NULL,    -­‐-­‐  localized  error  message        'Should  get  an  error  for  duplicate  nickname' ); SELECT  bag_eq(        'SELECT  *  FROM  users',        $$  VALUES                ('theory',        md5('foo'),    NOW()),                ('strongrrl',  md5('w00t'),  NOW())        $$,        'Should  still  have  just  the  two  users' ); test/insert_use        'Both  users  should  be  present'   ); SELECT  finish(); ROLLBACK;
  341. deploy/insert_u deploy/insert_user.sql -­‐-­‐  Deploy  insert_user   -­‐-­‐  requires:  users  

    -­‐-­‐  requires:  appschema   ! BEGIN; -­‐-­‐  XXX  Add  DDLs  here. COMMIT;
  342. deploy/insert_u deploy/insert_user.sql -­‐-­‐  Deploy  insert_user   -­‐-­‐  requires:  users  

    -­‐-­‐  requires:  appschema   ! BEGIN; CREATE  OR  REPLACE  FUNCTION  flipr.insert_user(        nickname  TEXT,        password  TEXT )  RETURNS  VOID  LANGUAGE  SQL  SECURITY  DEFINER  AS  $$        INSERT  INTO  flipr.users  VALUES($1,  md5($2)); $$; COMMIT;
  343. revert/insert_u revert/insert_user.sql -­‐-­‐  Revert  insert_user   ! BEGIN;   !

    ! ! COMMIT; DROP  FUNCTION  flipr.insert_user(TEXT,  TEXT);
  344. verify/insert_u verify/insert_user.sql -­‐-­‐  Verify  insert_user   ! BEGIN; SELECT  has_function_privilege(

           'flipr.insert_user(text,  text)',        'execute' ); ROLLBACK;
  345. verify/insert_u verify/insert_user.sql -­‐-­‐  Verify  insert_user   ! BEGIN; SELECT  has_function_privilege(

           'flipr.insert_user(text,  text)',        'execute' ); ROLLBACK; Convenient!
  346. We Good?    emacs  verify/insert_user.sql   > >    sqitch

     deploy Deploying  changes  to  flipr_test    +  insert_user  ..  ok >
  347. We Good?    emacs  verify/insert_user.sql   > >    sqitch

     deploy Deploying  changes  to  flipr_test    +  insert_user  ..  ok >    pg_prove  -­‐d  flipr_test  test/*.sql                     test/appschema.sql  ....  ok       test/insert_user.sql  ..  ok           test/users.sql  ........  ok         All  tests  successful. Files=3,  Tests=27,    0  wallclock  secs Result:  PASS >
  348.    git  add  . >  git  commit  -­‐m  'Add  `insert_user()`.'

    [userfuncs  e1c7769]  Add  `insert_user()`.  5  files  changed,  101  insertions(+)  create  mode  100644  deploy/insert_user.sql  create  mode  100644  revert/insert_user.sql  create  mode  100644  test/insert_user.sql  create  mode  100644  verify/insert_user.sql > Commitment >
  349.    git  push  origin  -­‐-­‐set-­‐upstream  userfuncs Counting  objects:  17,  done.

    Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (11/11),  done. Writing  objects:  100%  (11/11),  1.91  KiB,  done. Total  11  (delta  1),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git  *  [new  branch]            userfuncs  -­‐>  userfuncs Branch  userfuncs  set  up  to  track  remote  branch   userfuncs  from  origin. > Push It Real Good… >
  350.    git  push  origin  -­‐-­‐set-­‐upstream  userfuncs Counting  objects:  17,  done.

    Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (11/11),  done. Writing  objects:  100%  (11/11),  1.91  KiB,  done. Total  11  (delta  1),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git  *  [new  branch]            userfuncs  -­‐>  userfuncs Branch  userfuncs  set  up  to  track  remote  branch   userfuncs  from  origin. > Push It Real Good… >
  351.    git  push  origin  -­‐-­‐set-­‐upstream  userfuncs Counting  objects:  17,  done.

    Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (11/11),  done. Writing  objects:  100%  (11/11),  1.91  KiB,  done. Total  11  (delta  1),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git  *  [new  branch]            userfuncs  -­‐>  userfuncs Branch  userfuncs  set  up  to  track  remote  branch   userfuncs  from  origin. > Push It Real Good… >
  352. Reset, Mes Amis! git checkout users git reset --hard upstream/users

    git checkout -b userfuncs antisocial network
  353. Reset, Mes Amis! git checkout users git reset --hard upstream/users

    git checkout -b userfuncs git reset --hard insert_user antisocial network
  354. antisocial network None Shall Pass Create change_pass() Params: nickname old_pass

    new_pass Only update if old pass correct Use TDDD https:/ /github.com/ theory/agile-flipr.git
  355. Resets > My solution >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD

     is  now  at  048017a  Add  `change_pass()`.  
  356. Resets > >  sqitch  checkout  master  -­‐y Last  change  before

     the  branches  diverged:  appschema Reverting  changes  to  appschema  from  flipr_test    -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok Switched  to  branch  'master' Nothing  to  deploy  (up-­‐to-­‐date) > >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD  is  now  at  048017a  Add  `change_pass()`.  
  357. Resets > >  sqitch  checkout  master  -­‐y Last  change  before

     the  branches  diverged:  appschema Reverting  changes  to  appschema  from  flipr_test    -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok Switched  to  branch  'master' Nothing  to  deploy  (up-­‐to-­‐date) > >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD  is  now  at  048017a  Add  `change_pass()`.  
  358. Resets > >  sqitch  checkout  master  -­‐y Last  change  before

     the  branches  diverged:  appschema Reverting  changes  to  appschema  from  flipr_test    -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok Switched  to  branch  'master' Nothing  to  deploy  (up-­‐to-­‐date) > >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD  is  now  at  048017a  Add  `change_pass()`.  
  359. Resets > >  sqitch  checkout  master  -­‐y Last  change  before

     the  branches  diverged:  appschema Reverting  changes  to  appschema  from  flipr_test    -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok Switched  to  branch  'master' Nothing  to  deploy  (up-­‐to-­‐date) > >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD  is  now  at  048017a  Add  `change_pass()`.  
  360. Resets > >  sqitch  checkout  master  -­‐y Last  change  before

     the  branches  diverged:  appschema Reverting  changes  to  appschema  from  flipr_test    -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok Switched  to  branch  'master' Nothing  to  deploy  (up-­‐to-­‐date) > >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD  is  now  at  048017a  Add  `change_pass()`.  
  361. Resets > >  sqitch  checkout  master  -­‐y Last  change  before

     the  branches  diverged:  appschema Reverting  changes  to  appschema  from  flipr_test    -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok Switched  to  branch  'master' Nothing  to  deploy  (up-­‐to-­‐date) > >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD  is  now  at  048017a  Add  `change_pass()`.  
  362. Resets > >  sqitch  checkout  master  -­‐y Last  change  before

     the  branches  diverged:  appschema Reverting  changes  to  appschema  from  flipr_test    -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok Switched  to  branch  'master' Nothing  to  deploy  (up-­‐to-­‐date) >    git  reset  -­‐-­‐hard  appschema HEAD  is  now  at  e46bdf9  Add  appschema  test. > >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD  is  now  at  048017a  Add  `change_pass()`.  
  363. Resets > Known good. >  sqitch  checkout  master  -­‐y Last

     change  before  the  branches  diverged:  appschema Reverting  changes  to  appschema  from  flipr_test    -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok Switched  to  branch  'master' Nothing  to  deploy  (up-­‐to-­‐date) >    git  reset  -­‐-­‐hard  appschema HEAD  is  now  at  e46bdf9  Add  appschema  test. > >  git  reset  -­‐-­‐hard  upstream/change_pass HEAD  is  now  at  048017a  Add  `change_pass()`.  
  364.    git  merge  -­‐-­‐no-­‐ff  users  -­‐m  'Merge  branch  "users".' Merge

     made  by  the  'recursive'  strategy.  deploy/users.sql  |  13  +++++++++++++  revert/users.sql  |    7  +++++++  sqitch.plan            |    1  +  test/users.sql      |  30  ++++++++++++++++++++++++++++++  verify/users.sql  |    9  +++++++++  5  files  changed,  60  insertions(+)  create  mode  100644  deploy/users.sql  create  mode  100644  revert/users.sql  create  mode  100644  test/users.sql  create  mode  100644  verify/users.sql > Mergers and Acquisitions >
  365.    git  merge  -­‐-­‐no-­‐ff  users  -­‐m  'Merge  branch  "users".' Merge

     made  by  the  'recursive'  strategy.  deploy/users.sql  |  13  +++++++++++++  revert/users.sql  |    7  +++++++  sqitch.plan            |    1  +  test/users.sql      |  30  ++++++++++++++++++++++++++++++  verify/users.sql  |    9  +++++++++  5  files  changed,  60  insertions(+)  create  mode  100644  deploy/users.sql  create  mode  100644  revert/users.sql  create  mode  100644  test/users.sql  create  mode  100644  verify/users.sql > Mergers and Acquisitions > So far…
  366. >  git  merge  -­‐-­‐no-­‐ff  flips  -­‐m  'Merge  branch  "flips".' Merge

     made  by  the  'recursive'  strategy.  deploy/flips.sql  |  15  +++++++++++++++  revert/flips.sql  |    7  +++++++  sqitch.plan            |    1  +  test/flips.sql      |  39  +++++++++++++++++++++++++++++++++++++  verify/flips.sql  |  12  ++++++++++++  5  files  changed,  74  insertions(+)  create  mode  100644  deploy/flips.sql  create  mode  100644  revert/flips.sql  create  mode  100644  test/flips.sql  create  mode  100644  verify/flips.sql > Mergers and Acquisitions >
  367. >  git  merge  -­‐-­‐no-­‐ff  flips  -­‐m  'Merge  branch  "flips".' Merge

     made  by  the  'recursive'  strategy.  deploy/flips.sql  |  15  +++++++++++++++  revert/flips.sql  |    7  +++++++  sqitch.plan            |    1  +  test/flips.sql      |  39  +++++++++++++++++++++++++++++++++++++  verify/flips.sql  |  12  ++++++++++++  5  files  changed,  74  insertions(+)  create  mode  100644  deploy/flips.sql  create  mode  100644  revert/flips.sql  create  mode  100644  test/flips.sql  create  mode  100644  verify/flips.sql > Mergers and Acquisitions > So good…
  368. >  git  merge  -­‐-­‐no-­‐ff  userfuncs  -­‐m  'Merge  branch  "user Auto-­‐merging

     sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Automatic  merge  failed;  fix  conflicts  and  then   commit  the  result. > Mergers and Acquisitions >
  369. >  git  merge  -­‐-­‐no-­‐ff  userfuncs  -­‐m  'Merge  branch  "user Auto-­‐merging

     sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Automatic  merge  failed;  fix  conflicts  and  then   commit  the  result. > Mergers and Acquisitions >
  370. >  git  merge  -­‐-­‐no-­‐ff  userfuncs  -­‐m  'Merge  branch  "user Auto-­‐merging

     sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Automatic  merge  failed;  fix  conflicts  and  then   commit  the  result. > Mergers and Acquisitions >
  371. >  git  merge  -­‐-­‐no-­‐ff  userfuncs  -­‐m  'Merge  branch  "user Auto-­‐merging

     sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Automatic  merge  failed;  fix  conflicts  and  then   commit  the  result. > Mergers and Acquisitions > Wha???
  372. Back in Time…    git  checkout  -­‐b  userfuncs  users Switched

     to  a  new  branch  'userfuncs' > > Ah-ha!
  373. Branching Out users branched from master flips branched from users

    userfuncs branched from users antisocial network
  374. Branching Out users branched from master flips branched from users

    userfuncs branched from users users and flips merged to master antisocial network
  375. Branching Out users branched from master flips branched from users

    userfuncs branched from users users and flips merged to master userfuncs unaware of flips antisocial network
  376. Backbrancher master users A B flips C userfuncs D branch

    branch branch E Add change_pass function
  377. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D branch branch branch E
  378. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D F merge D & E branch branch branch E
  379. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D No C! F merge D & E branch branch branch E
  380. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D conflict! branch branch branch E
  381. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D conflict! Now what? branch branch branch E
  382. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch D E
  383. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch Last common with master: B
  384. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch C
  385. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch C D Rebased E
  386. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch C D E merge D & E E
  387. \o/ Rebase master master users A B flips C userfuncs

    B merge B C merge C branch branch branch C D E merge D & E E
  388.    git  reset  -­‐-­‐hard  HEAD HEAD  is  now  at  e1cfc5d

     Merge  branch  "flips". > Reset > Use with care!
  389.    git  reset  -­‐-­‐hard  HEAD HEAD  is  now  at  e1cfc5d

     Merge  branch  "flips". > Reset >    git  checkout  userfuncs Switched  to  branch  'userfuncs' >
  390. >  git  rebase  master First,  rewinding  head  to  replay  your

     work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Failed  to  merge  in  the  changes. Patch  failed  at  0001  Add  `insert_user()`. The  copy  of  the  patch  that  failed  is  found  in:      flipr-­‐db/.git/rebase-­‐apply/patch When  you  have  resolved  this  problem,  run  "git  rebase  -­‐-­‐contin If  you  prefer  to  skip  this  patch,  run  "git  rebase  -­‐-­‐skip"  ins To  check  out  the  original  branch  and  stop  rebasing,  run  "git   rebase  -­‐-­‐abort". > Rebase >
  391. >  git  rebase  master First,  rewinding  head  to  replay  your

     work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Failed  to  merge  in  the  changes. Patch  failed  at  0001  Add  `insert_user()`. The  copy  of  the  patch  that  failed  is  found  in:      flipr-­‐db/.git/rebase-­‐apply/patch When  you  have  resolved  this  problem,  run  "git  rebase  -­‐-­‐contin If  you  prefer  to  skip  this  patch,  run  "git  rebase  -­‐-­‐skip"  ins To  check  out  the  original  branch  and  stop  rebasing,  run  "git   rebase  -­‐-­‐abort". > Rebase > Back to B, apply C.
  392. >  git  rebase  master First,  rewinding  head  to  replay  your

     work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Failed  to  merge  in  the  changes. Patch  failed  at  0001  Add  `insert_user()`. The  copy  of  the  patch  that  failed  is  found  in:      flipr-­‐db/.git/rebase-­‐apply/patch When  you  have  resolved  this  problem,  run  "git  rebase  -­‐-­‐contin If  you  prefer  to  skip  this  patch,  run  "git  rebase  -­‐-­‐skip"  ins To  check  out  the  original  branch  and  stop  rebasing,  run  "git   rebase  -­‐-­‐abort". > Rebase > That’s D
  393. >  git  rebase  master First,  rewinding  head  to  replay  your

     work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Failed  to  merge  in  the  changes. Patch  failed  at  0001  Add  `insert_user()`. The  copy  of  the  patch  that  failed  is  found  in:      flipr-­‐db/.git/rebase-­‐apply/patch When  you  have  resolved  this  problem,  run  "git  rebase  -­‐-­‐contin If  you  prefer  to  skip  this  patch,  run  "git  rebase  -­‐-­‐skip"  ins To  check  out  the  original  branch  and  stop  rebasing,  run  "git   rebase  -­‐-­‐abort". > Rebase > Here comes D…
  394. >  git  rebase  master First,  rewinding  head  to  replay  your

     work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Failed  to  merge  in  the  changes. Patch  failed  at  0001  Add  `insert_user()`. The  copy  of  the  patch  that  failed  is  found  in:      flipr-­‐db/.git/rebase-­‐apply/patch When  you  have  resolved  this  problem,  run  "git  rebase  -­‐-­‐contin If  you  prefer  to  skip  this  patch,  run  "git  rebase  -­‐-­‐skip"  ins To  check  out  the  original  branch  and  stop  rebasing,  run  "git   rebase  -­‐-­‐abort". > Rebase > D’oh!
  395. >  git  rebase  master First,  rewinding  head  to  replay  your

     work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan CONFLICT  (content):  Merge  conflict  in  sqitch.plan Failed  to  merge  in  the  changes. Patch  failed  at  0001  Add  `insert_user()`. The  copy  of  the  patch  that  failed  is  found  in:      flipr-­‐db/.git/rebase-­‐apply/patch When  you  have  resolved  this  problem,  run  "git  rebase  -­‐-­‐contin If  you  prefer  to  skip  this  patch,  run  "git  rebase  -­‐-­‐skip"  ins To  check  out  the  original  branch  and  stop  rebasing,  run  "git   rebase  -­‐-­‐abort". > Rebase >
  396. >  git  diff diff  -­‐-­‐cc  sqitch.plan index  7526d1c,e2c966c..0000000 -­‐-­‐-­‐  a/sqitch.plan

    +++  b/sqitch.plan @@@  -­‐4,4  -­‐4,4  +4,8  @@@        appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler  <d    users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wh ++<<<<<<<  HEAD  +flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David ++======= +  insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z ++>>>>>>>  Add  `insert_user()`. Wha Happen? >
  397. >  git  diff diff  -­‐-­‐cc  sqitch.plan index  7526d1c,e2c966c..0000000 -­‐-­‐-­‐  a/sqitch.plan

    +++  b/sqitch.plan @@@  -­‐4,4  -­‐4,4  +4,8  @@@        appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler  <d    users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wh ++<<<<<<<  HEAD  +flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David ++======= +  insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z ++>>>>>>>  Add  `insert_user()`. Wha Happen? >
  398. >  git  diff diff  -­‐-­‐cc  sqitch.plan index  7526d1c,e2c966c..0000000 -­‐-­‐-­‐  a/sqitch.plan

    +++  b/sqitch.plan @@@  -­‐4,4  -­‐4,4  +4,8  @@@        appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler  <d    users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wh ++<<<<<<<  HEAD  +flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David ++======= +  insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z ++>>>>>>>  Add  `insert_user()`. Wha Happen? > B
  399. >  git  diff diff  -­‐-­‐cc  sqitch.plan index  7526d1c,e2c966c..0000000 -­‐-­‐-­‐  a/sqitch.plan

    +++  b/sqitch.plan @@@  -­‐4,4  -­‐4,4  +4,8  @@@        appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler  <d    users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wh ++<<<<<<<  HEAD  +flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David ++======= +  insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z ++>>>>>>>  Add  `insert_user()`. Wha Happen? > C B
  400. >  git  diff diff  -­‐-­‐cc  sqitch.plan index  7526d1c,e2c966c..0000000 -­‐-­‐-­‐  a/sqitch.plan

    +++  b/sqitch.plan @@@  -­‐4,4  -­‐4,4  +4,8  @@@        appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler  <d    users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wh ++<<<<<<<  HEAD  +flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David ++======= +  insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z ++>>>>>>>  Add  `insert_user()`. Wha Happen? > C D B
  401. Scratch that Sqitch Screwed either way? Fortunately, this is Git

    Tell it to treat Sqitch plans differently antisocial network
  402. Scratch that Sqitch Screwed either way? Fortunately, this is Git

    Tell it to treat Sqitch plans differently Changes on single lines antisocial network
  403. Scratch that Sqitch Screwed either way? Fortunately, this is Git

    Tell it to treat Sqitch plans differently Changes on single lines Only appended to plan file antisocial network
  404. Scratch that Sqitch Screwed either way? Fortunately, this is Git

    Tell it to treat Sqitch plans differently Changes on single lines Only appended to plan file Use the “union” merge antisocial network
  405. Re: Union Merge Run 3-way file level merge for text

    files, but take lines from both versions, instead of leaving conflict markers. This tends to leave the added lines in the resulting file in random order and the user should verify the result. Do not use this if you do not understand the implications. —Git Manual
  406. Hallelunion Just appends lines Exactly how changes work Let’s clean

    up our mess And try again antisocial network
  407.    echo  sqitch.plan  merge=union  >  .gitattributes >    git  rebase

     master First,  rewinding  head  to  replay  your  work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan Applying:  Add  `change_pass()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan >    git  rebase  -­‐-­‐abort > Reemerge >
  408.    echo  sqitch.plan  merge=union  >  .gitattributes >    git  rebase

     master First,  rewinding  head  to  replay  your  work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan Applying:  Add  `change_pass()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan >    git  rebase  -­‐-­‐abort > Reemerge > Back to B, applies C
  409.    echo  sqitch.plan  merge=union  >  .gitattributes >    git  rebase

     master First,  rewinding  head  to  replay  your  work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan Applying:  Add  `change_pass()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan >    git  rebase  -­‐-­‐abort > Reemerge > That’s D
  410.    echo  sqitch.plan  merge=union  >  .gitattributes >    git  rebase

     master First,  rewinding  head  to  replay  your  work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan Applying:  Add  `change_pass()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan >    git  rebase  -­‐-­‐abort > Reemerge > Union merge
  411.    echo  sqitch.plan  merge=union  >  .gitattributes >    git  rebase

     master First,  rewinding  head  to  replay  your  work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan Applying:  Add  `change_pass()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan >    git  rebase  -­‐-­‐abort > Reemerge > That’s E
  412.    echo  sqitch.plan  merge=union  >  .gitattributes >    git  rebase

     master First,  rewinding  head  to  replay  your  work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan Applying:  Add  `change_pass()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan >    git  rebase  -­‐-­‐abort > Reemerge > Union merge
  413.    echo  sqitch.plan  merge=union  >  .gitattributes >    git  rebase

     master First,  rewinding  head  to  replay  your  work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan Applying:  Add  `change_pass()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan >    git  rebase  -­‐-­‐abort > Reemerge > Success!
  414.    echo  sqitch.plan  merge=union  >  .gitattributes >    git  rebase

     master First,  rewinding  head  to  replay  your  work  on  top  of  it... Applying:  Add  `insert_user()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan Applying:  Add  `change_pass()`. Using  index  info  to  reconstruct  a  base  tree... M   sqitch.plan Falling  back  to  patching  base  and  3-­‐way  merge... Auto-­‐merging  sqitch.plan >    git  rebase  -­‐-­‐abort > Reemerge >    emacs  sqitch.plan > Success!
  415. sqitch.plan What’s the Plan, Man? %syntax-­‐version=1.0.0-­‐b2   %project=flipr   %uri=https://github.com/theory/agile-­‐flipr

      ! appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler   <[email protected]>  #  Adds  flipr  app  schema.   users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wheeler   <[email protected]>  #  Creates  table  to  track  our   users.   flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David  E.   Wheeler  <[email protected]>  #  Adds  table  for  storing   flips.   insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to   insert  a  user.   change_pass  [users  appschema]  2014-­‐01-­‐08T05:23:28Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to
  416. sqitch.plan What’s the Plan, Man? %syntax-­‐version=1.0.0-­‐b2   %project=flipr   %uri=https://github.com/theory/agile-­‐flipr

      ! appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler   <[email protected]>  #  Adds  flipr  app  schema.   users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wheeler   <[email protected]>  #  Creates  table  to  track  our   users.   flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David  E.   Wheeler  <[email protected]>  #  Adds  table  for  storing   flips.   insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to   insert  a  user.   change_pass  [users  appschema]  2014-­‐01-­‐08T05:23:28Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to
  417. sqitch.plan What’s the Plan, Man? %syntax-­‐version=1.0.0-­‐b2   %project=flipr   %uri=https://github.com/theory/agile-­‐flipr

      ! appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler   <[email protected]>  #  Adds  flipr  app  schema.   users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wheeler   <[email protected]>  #  Creates  table  to  track  our   users.   flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David  E.   Wheeler  <[email protected]>  #  Adds  table  for  storing   flips.   insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to   insert  a  user.   change_pass  [users  appschema]  2014-­‐01-­‐08T05:23:28Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to
  418. sqitch.plan What’s the Plan, Man? %syntax-­‐version=1.0.0-­‐b2   %project=flipr   %uri=https://github.com/theory/agile-­‐flipr

      ! appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler   <[email protected]>  #  Adds  flipr  app  schema.   users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wheeler   <[email protected]>  #  Creates  table  to  track  our   users.   flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David  E.   Wheeler  <[email protected]>  #  Adds  table  for  storing   flips.   insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to   insert  a  user.   change_pass  [users  appschema]  2014-­‐01-­‐08T05:23:28Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to
  419. sqitch.plan What’s the Plan, Man? %syntax-­‐version=1.0.0-­‐b2   %project=flipr   %uri=https://github.com/theory/agile-­‐flipr

      ! appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler   <[email protected]>  #  Adds  flipr  app  schema.   users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wheeler   <[email protected]>  #  Creates  table  to  track  our   users.   flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David  E.   Wheeler  <[email protected]>  #  Adds  table  for  storing   flips.   insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to   insert  a  user.   change_pass  [users  appschema]  2014-­‐01-­‐08T05:23:28Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to
  420. sqitch.plan What’s the Plan, Man? %syntax-­‐version=1.0.0-­‐b2   %project=flipr   %uri=https://github.com/theory/agile-­‐flipr

      ! appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler   <[email protected]>  #  Adds  flipr  app  schema.   users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wheeler   <[email protected]>  #  Creates  table  to  track  our   users.   flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David  E.   Wheeler  <[email protected]>  #  Adds  table  for  storing   flips.   insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to   insert  a  user.   change_pass  [users  appschema]  2014-­‐01-­‐08T05:23:28Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to
  421. sqitch.plan What’s the Plan, Man? %syntax-­‐version=1.0.0-­‐b2   %project=flipr   %uri=https://github.com/theory/agile-­‐flipr

      ! appschema  2014-­‐01-­‐07T23:47:31Z  David  E.  Wheeler   <[email protected]>  #  Adds  flipr  app  schema.   users  [appschema]  2014-­‐01-­‐08T04:13:38Z  David  E.  Wheeler   <[email protected]>  #  Creates  table  to  track  our   users.   flips  [appschema  users]  2014-­‐01-­‐08T04:48:29Z  David  E.   Wheeler  <[email protected]>  #  Adds  table  for  storing   flips.   insert_user  [users  appschema]  2014-­‐01-­‐08T04:52:50Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to   insert  a  user.   change_pass  [users  appschema]  2014-­‐01-­‐08T05:23:28Z  David   E.  Wheeler  <[email protected]>  #  Creates  a  function  to Perfect
  422. >  sqitch  rebase  -­‐y Reverting  all  changes  from  flipr_test  

     -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok    -­‐  appschema  ....  ok Deploying  changes  to  flipr_test    +  appschema  ....  ok    +  users  ........  ok    +  flips  ........  ok    +  insert_user  ..  ok    +  change_pass  ..  ok > Work It >
  423. >  sqitch  rebase  -­‐y Reverting  all  changes  from  flipr_test  

     -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok    -­‐  appschema  ....  ok Deploying  changes  to  flipr_test    +  appschema  ....  ok    +  users  ........  ok    +  flips  ........  ok    +  insert_user  ..  ok    +  change_pass  ..  ok > Work It > {
  424. >  sqitch  rebase  -­‐y Reverting  all  changes  from  flipr_test  

     -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok    -­‐  appschema  ....  ok Deploying  changes  to  flipr_test    +  appschema  ....  ok    +  users  ........  ok    +  flips  ........  ok    +  insert_user  ..  ok    +  change_pass  ..  ok > Work It > { {
  425. >  sqitch  rebase  -­‐y Reverting  all  changes  from  flipr_test  

     -­‐  change_pass  ..  ok    -­‐  insert_user  ..  ok    -­‐  users  ........  ok    -­‐  appschema  ....  ok Deploying  changes  to  flipr_test    +  appschema  ....  ok    +  users  ........  ok    +  flips  ........  ok    +  insert_user  ..  ok    +  change_pass  ..  ok > Work It > { { Awesomsauce
  426. >  git  add  . >    git  commit  -­‐m  'Use

     union  merge  for  `sqitch.plan`.' [userfuncs  f38ef5c]  Use  union  merge  for  `sqitch.plan`.  1  file  changed,  1  insertion(+)  create  mode  100644  .gitattributes > Make it So >
  427. >  git  add  . >    git  commit  -­‐m  'Use

     union  merge  for  `sqitch.plan`.' [userfuncs  f38ef5c]  Use  union  merge  for  `sqitch.plan`.  1  file  changed,  1  insertion(+)  create  mode  100644  .gitattributes >    git  push  -­‐-­‐force  origin  userfuncs Counting  objects:  31,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (24/24),  done. Writing  objects:  100%  (25/25),  3.83  KiB,  done. Total  25  (delta  7),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git  +  1813323...f38ef5c  userfuncs  -­‐>  userfuncs  (forced  upda > Make it So > Overwrite history
  428. >  git  add  . >    git  commit  -­‐m  'Use

     union  merge  for  `sqitch.plan`.' [userfuncs  f38ef5c]  Use  union  merge  for  `sqitch.plan`.  1  file  changed,  1  insertion(+)  create  mode  100644  .gitattributes >    git  push  -­‐-­‐force  origin  userfuncs Counting  objects:  31,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (24/24),  done. Writing  objects:  100%  (25/25),  3.83  KiB,  done. Total  25  (delta  7),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git  +  1813323...f38ef5c  userfuncs  -­‐>  userfuncs  (forced  upda > Make it So >
  429. >

  430.    git  merge  -­‐-­‐no-­‐ff  userfuncs  -­‐m  'Merge  branch  "userfuncs".'  

                Merge  made  by  the  'recursive'  strategy.  .gitattributes                  |    1  +  deploy/change_pass.sql  |  21  ++++++++++++++++++  deploy/insert_user.sql  |  14  ++++++++++++  revert/change_pass.sql  |    7  ++++++  revert/insert_user.sql  |    7  ++++++  sqitch.plan                        |    2  ++  test/change_pass.sql      |  52  ++++++++++++++++++++++++++++++++++  test/insert_user.sql      |  69  ++++++++++++++++++++++++++++++++++  verify/change_pass.sql  |    7  ++++++  verify/insert_user.sql  |  10  +++++++++  10  files  changed,  190  insertions(+)  create  mode  100644  .gitattributes  create  mode  100644  deploy/change_pass.sql  create  mode  100644  deploy/insert_user.sql  create  mode  100644  revert/change_pass.sql  create  mode  100644  revert/insert_user.sql  create  mode  100644  test/change_pass.sql  create  mode  100644  test/insert_user.sql  create  mode  100644  verify/change_pass.sql  create  mode  100644  verify/insert_user.sql > >  git  checkout  master Switched  to  branch  'master' > >
  431.    git  merge  -­‐-­‐no-­‐ff  userfuncs  -­‐m  'Merge  branch  "userfuncs".'  

                Merge  made  by  the  'recursive'  strategy.  .gitattributes                  |    1  +  deploy/change_pass.sql  |  21  ++++++++++++++++++  deploy/insert_user.sql  |  14  ++++++++++++  revert/change_pass.sql  |    7  ++++++  revert/insert_user.sql  |    7  ++++++  sqitch.plan                        |    2  ++  test/change_pass.sql      |  52  ++++++++++++++++++++++++++++++++++  test/insert_user.sql      |  69  ++++++++++++++++++++++++++++++++++  verify/change_pass.sql  |    7  ++++++  verify/insert_user.sql  |  10  +++++++++  10  files  changed,  190  insertions(+)  create  mode  100644  .gitattributes  create  mode  100644  deploy/change_pass.sql  create  mode  100644  deploy/insert_user.sql  create  mode  100644  revert/change_pass.sql  create  mode  100644  revert/insert_user.sql  create  mode  100644  test/change_pass.sql  create  mode  100644  test/insert_user.sql  create  mode  100644  verify/change_pass.sql  create  mode  100644  verify/insert_user.sql > >  git  checkout  master Switched  to  branch  'master' > > \O/
  432.    git  push Counting  objects:  1,  done. Writing  objects:  100%

     (1/1),  231  bytes,  done. Total  1  (delta  0),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      e1cfc5d..7c5937a    master  -­‐>  master > Pusher >
  433. Ship Shape Good work so far People gonna flip out

    Let’s tag a dev release antisocial network
  434. Ship Shape Good work so far People gonna flip out

    Let’s tag a dev release Bundle it up antisocial network
  435. Ship Shape Good work so far People gonna flip out

    Let’s tag a dev release Bundle it up And ship it antisocial network
  436.    git  commit  -­‐am  'Tag  the  database  @v1.0.0-­‐r1.' [master  dce7606]

     Tag  the  database  @v1.0.0-­‐r1.  1  file  changed,  1  insertion(+) >    sqitch  tag  v1.0.0-­‐r1  -­‐n  'Tag  v1.0.0-­‐r1.' Tagged  "change_pass"  with  @v1.0.0-­‐r1 > You’re It >
  437.    git  tag  v1.0.0-­‐r1  -­‐am  'Tag  v1.0.0-­‐r1.' >    git

     commit  -­‐am  'Tag  the  database  @v1.0.0-­‐r1.' [master  dce7606]  Tag  the  database  @v1.0.0-­‐r1.  1  file  changed,  1  insertion(+) >    sqitch  tag  v1.0.0-­‐r1  -­‐n  'Tag  v1.0.0-­‐r1.' Tagged  "change_pass"  with  @v1.0.0-­‐r1 > You’re It > In sync. In sync.
  438.    git  tag  v1.0.0-­‐r1  -­‐am  'Tag  v1.0.0-­‐r1.' >    git

     commit  -­‐am  'Tag  the  database  @v1.0.0-­‐r1.' [master  dce7606]  Tag  the  database  @v1.0.0-­‐r1.  1  file  changed,  1  insertion(+) >    sqitch  tag  v1.0.0-­‐r1  -­‐n  'Tag  v1.0.0-­‐r1.' Tagged  "change_pass"  with  @v1.0.0-­‐r1 >    git  push Counting  objects:  5,  done. Writing  objects:  100%  (3/3),  344  bytes,  done. Total  3  (delta  2),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      7c5937a..dce7606    master  -­‐>  master > You’re It >
  439.    git  tag  v1.0.0-­‐r1  -­‐am  'Tag  v1.0.0-­‐r1.' >    git

     commit  -­‐am  'Tag  the  database  @v1.0.0-­‐r1.' [master  dce7606]  Tag  the  database  @v1.0.0-­‐r1.  1  file  changed,  1  insertion(+) >    sqitch  tag  v1.0.0-­‐r1  -­‐n  'Tag  v1.0.0-­‐r1.' Tagged  "change_pass"  with  @v1.0.0-­‐r1 >    git  push Counting  objects:  5,  done. Writing  objects:  100%  (3/3),  344  bytes,  done. Total  3  (delta  2),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      7c5937a..dce7606    master  -­‐>  master >    git  push  -­‐-­‐tags To  https://github.com/theory/agile-­‐flipr.git  *  [new  tag]                  v1.0.0-­‐r1  -­‐>  v1.0.0-­‐r1 > You’re It >
  440.    sqitch  bundle  -­‐-­‐dest-­‐dir  flipr-­‐1.0.0-­‐r1 Bundling  into  flipr-­‐1.0.0-­‐r1 Writing  config

    Writing  plan Writing  scripts    +  appschema    +  users    +  flips    +  insert_user    +  change_pass  @v1.0.0-­‐r1 > Bundle Up >
  441.    sqitch  bundle  -­‐-­‐dest-­‐dir  flipr-­‐1.0.0-­‐r1 Bundling  into  flipr-­‐1.0.0-­‐r1 Writing  config

    Writing  plan Writing  scripts    +  appschema    +  users    +  flips    +  insert_user    +  change_pass  @v1.0.0-­‐r1 > Bundle Up > {
  442.    createdb  flipr_qa   > >  cd  flipr-­‐1.0.0-­‐r1 > Bundled?

    >    sqitch  deploy  db:pg:flipr_qa Adding  registry  tables  to  db:pg:flipr_qa Deploying  changes  to  db:pg:flipr_qa    +  appschema  ...............  ok    +  users  ...................  ok    +  flips  ...................  ok    +  insert_user  .............  ok    +  change_pass  @v1.0.0-­‐r1  ..  ok >
  443.    createdb  flipr_qa   > >  cd  flipr-­‐1.0.0-­‐r1 > Bundled?

    > Ship it!    sqitch  deploy  db:pg:flipr_qa Adding  registry  tables  to  db:pg:flipr_qa Deploying  changes  to  db:pg:flipr_qa    +  appschema  ...............  ok    +  users  ...................  ok    +  flips  ...................  ok    +  insert_user  .............  ok    +  change_pass  @v1.0.0-­‐r1  ..  ok >
  444. antisocial network Merge Madness Merge everything Back to master users

    flips userfuncs Union merge sqitch.plan Bundle and ship
  445. antisocial network Merge Madness Merge everything Back to master users

    flips userfuncs Union merge sqitch.plan Bundle and ship https:/ /github.com/ theory/agile-flipr.git
  446. Ruh-Roh > >  git  reset  -­‐-­‐hard  reltag HEAD  is  now

     at  dce7606  Tag  the  database  @v1.0.0-­‐r1. >
  447.    psql  -­‐d  flipr_test  -­‐c  "        SELECT

     flipr.insert_user('foo',  'secr3t'),                      flipr.insert_user('bar',  'secr3t');        SELECT  nickname,  password  FROM  flipr.users; "  nickname  |                          password                           -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  foo            |  9695da4dd567a19f9b92065f240c6725  bar            |  9695da4dd567a19f9b92065f240c6725 (2  rows) > Ruh-Roh > >  git  reset  -­‐-­‐hard  reltag HEAD  is  now  at  dce7606  Tag  the  database  @v1.0.0-­‐r1. > Same password
  448.    psql  -­‐d  flipr_test  -­‐c  "        SELECT

     flipr.insert_user('foo',  'secr3t'),                      flipr.insert_user('bar',  'secr3t');        SELECT  nickname,  password  FROM  flipr.users; "  nickname  |                          password                           -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  foo            |  9695da4dd567a19f9b92065f240c6725  bar            |  9695da4dd567a19f9b92065f240c6725 (2  rows) > Ruh-Roh > >  git  reset  -­‐-­‐hard  reltag HEAD  is  now  at  dce7606  Tag  the  database  @v1.0.0-­‐r1. >
  449.    psql  -­‐d  flipr_test  -­‐c  "        SELECT

     flipr.insert_user('foo',  'secr3t'),                      flipr.insert_user('bar',  'secr3t');        SELECT  nickname,  password  FROM  flipr.users; "  nickname  |                          password                           -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  foo            |  9695da4dd567a19f9b92065f240c6725  bar            |  9695da4dd567a19f9b92065f240c6725 (2  rows) > Ruh-Roh > Not good. >  git  reset  -­‐-­‐hard  reltag HEAD  is  now  at  dce7606  Tag  the  database  @v1.0.0-­‐r1. >
  450.    sqitch  add  pgcrypto  -­‐n  'Loads  pgcrypto  extension.' Adding  deploy/pgcrypto.sql

    Adding  revert/pgcrypto.sql Adding  test/pgcrypto.sql Adding  verify/pgcrypto.sql Added  "pgcrypto"  to  sqitch.plan > PGCryptonite >
  451.    sqitch  add  pgcrypto  -­‐n  'Loads  pgcrypto  extension.' Adding  deploy/pgcrypto.sql

    Adding  revert/pgcrypto.sql Adding  test/pgcrypto.sql Adding  verify/pgcrypto.sql Added  "pgcrypto"  to  sqitch.plan > PGCryptonite >    emacs  deploy/pgcrpyto.sql >
  452.    sqitch  add  pgcrypto  -­‐n  'Loads  pgcrypto  extension.'   Adding

     deploy/pgcrypto.sql   Adding  revert/pgcrypto.sql   Adding  test/pgcrypto.sql   Adding  verify/pgcrypto.sql   Added  "pgcrypto"  to  sqitch.plan   >  emacs  deploy/pgcrpyto.sql   > PGCryptonite >
  453.    sqitch  add  pgcrypto  -­‐n  'Loads  pgcrypto  extension.'   Adding

     deploy/pgcrypto.sql   Adding  revert/pgcrypto.sql   Adding  test/pgcrypto.sql   Adding  verify/pgcrypto.sql   Added  "pgcrypto"  to  sqitch.plan   >  emacs  deploy/pgcrpyto.sql   > PGCryptonite >    emacs  verify/pgcrpyto.sql >
  454. -­‐-­‐  Verify  pgcrypto   ! BEGIN; verify/pgcrypto verify/pgcrypto.sql COMMIT; SELECT

     1/count(*)  FROM  pg_extension  WHERE  extname  =  'pgcrypto'; SELECT  has_function_privilege('crypt(text,  text)',  'execute'); SELECT  has_function_privilege('gen_salt(text)',  'execute');
  455. -­‐-­‐  Verify  pgcrypto   ! BEGIN; verify/pgcrypto verify/pgcrypto.sql COMMIT; SELECT

     1/count(*)  FROM  pg_extension  WHERE  extname  =  'pgcrypto'; SELECT  has_function_privilege('crypt(text,  text)',  'execute'); SELECT  has_function_privilege('gen_salt(text)',  'execute');
  456. -­‐-­‐  Verify  pgcrypto   ! BEGIN; verify/pgcrypto verify/pgcrypto.sql COMMIT; SELECT

     1/count(*)  FROM  pg_extension  WHERE  extname  =  'pgcrypto'; SELECT  has_function_privilege('crypt(text,  text)',  'execute'); SELECT  has_function_privilege('gen_salt(text)',  'execute');
  457. You Know the Drill Write revert script Add test Use

    has_function() antisocial network
  458. You Know the Drill Write revert script Add test Use

    has_function() Commit antisocial network
  459. You Know the Drill Write revert script Add test Use

    has_function() Commit Push antisocial network
  460. You Know the Drill Write revert script Add test Use

    has_function() Commit Push Modify the insert_user test antisocial network
  461. >

  462. >    git  diff  test/insert_user.sql diff  -­‐-­‐git  a/test/insert_user.sql  b/test/insert_user.sql index

     225bb18..2a775a6  100644 -­‐-­‐-­‐  a/test/insert_user.sql +++  b/test/insert_user.sql @@  -­‐29,25  +29,25  @@  SELECT  lives_ok(          'Insert  a  user'  );   -­‐SELECT  row_eq( -­‐      'SELECT  *  FROM  users', -­‐      ROW('theory',  md5('foo'),  NOW())::users, -­‐      'The  user  should  have  been  inserted' -­‐); +SELECT  ok(  EXISTS( +        SELECT  1  FROM  flipr.users +          WHERE  nickname  =  'theory' +              AND  password  =  crypt('foo',  password) +),  'The  user  should  have  been  inserted'  );    SELECT  lives_ok(          $$  SELECT  insert_user('strongrrl',  'w00t')  $$,          'Insert  another  user'  );
  463. >    git  diff  test/insert_user.sql diff  -­‐-­‐git  a/test/insert_user.sql  b/test/insert_user.sql index

     225bb18..2a775a6  100644 -­‐-­‐-­‐  a/test/insert_user.sql +++  b/test/insert_user.sql @@  -­‐29,25  +29,25  @@  SELECT  lives_ok(          'Insert  a  user'  );   -­‐SELECT  row_eq( -­‐      'SELECT  *  FROM  users', -­‐      ROW('theory',  md5('foo'),  NOW())::users, -­‐      'The  user  should  have  been  inserted' -­‐); +SELECT  ok(  EXISTS( +        SELECT  1  FROM  flipr.users +          WHERE  nickname  =  'theory' +              AND  password  =  crypt('foo',  password) +),  'The  user  should  have  been  inserted'  );    SELECT  lives_ok(          $$  SELECT  insert_user('strongrrl',  'w00t')  $$,          'Insert  another  user'  );
  464. >    git  diff  test/insert_user.sql diff  -­‐-­‐git  a/test/insert_user.sql  b/test/insert_user.sql index

     225bb18..2a775a6  100644 -­‐-­‐-­‐  a/test/insert_user.sql +++  b/test/insert_user.sql @@  -­‐29,25  +29,25  @@  SELECT  lives_ok(          'Insert  a  user'  );   -­‐SELECT  row_eq( -­‐      'SELECT  *  FROM  users', -­‐      ROW('theory',  md5('foo'),  NOW())::users, -­‐      'The  user  should  have  been  inserted' -­‐); +SELECT  ok(  EXISTS( +        SELECT  1  FROM  flipr.users +          WHERE  nickname  =  'theory' +              AND  password  =  crypt('foo',  password) +),  'The  user  should  have  been  inserted'  );    SELECT  lives_ok(          $$  SELECT  insert_user('strongrrl',  'w00t')  $$,          'Insert  another  user'  );
  465. -­‐SELECT  bag_eq(   -­‐        'SELECT  *  FROM

     users',   -­‐        $$  VALUES   -­‐                ('theory',        md5('foo'),    NOW()),   -­‐                ('strongrrl',  md5('w00t'),  NOW())   -­‐        $$,   -­‐        'Both  users  should  be  present'   -­‐);   +SELECT  is(COUNT(*)::INT,  2,  'There  should  be  two  users')   +    FROM  flipr.users;   +   +SELECT  ok(  EXISTS(   +        SELECT  1  FROM  flipr.users   +          WHERE  nickname  =  'strongrrl'   +              AND  password  =  crypt('w00t',  password)   +),  'The  second  user  should  have  been  inserted'  );        SELECT  throws_ok(            $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,  
  466. -­‐SELECT  bag_eq(   -­‐        'SELECT  *  FROM

     users',   -­‐        $$  VALUES   -­‐                ('theory',        md5('foo'),    NOW()),   -­‐                ('strongrrl',  md5('w00t'),  NOW())   -­‐        $$,   -­‐        'Both  users  should  be  present'   -­‐);   +SELECT  is(COUNT(*)::INT,  2,  'There  should  be  two  users')   +    FROM  flipr.users;   +   +SELECT  ok(  EXISTS(   +        SELECT  1  FROM  flipr.users   +          WHERE  nickname  =  'strongrrl'   +              AND  password  =  crypt('w00t',  password)   +),  'The  second  user  should  have  been  inserted'  );        SELECT  throws_ok(            $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,  
  467. -­‐SELECT  bag_eq(   -­‐        'SELECT  *  FROM

     users',   -­‐        $$  VALUES   -­‐                ('theory',        md5('foo'),    NOW()),   -­‐                ('strongrrl',  md5('w00t'),  NOW())   -­‐        $$,   -­‐        'Both  users  should  be  present'   -­‐);   +SELECT  is(COUNT(*)::INT,  2,  'There  should  be  two  users')   +    FROM  flipr.users;   +   +SELECT  ok(  EXISTS(   +        SELECT  1  FROM  flipr.users   +          WHERE  nickname  =  'strongrrl'   +              AND  password  =  crypt('w00t',  password)   +),  'The  second  user  should  have  been  inserted'  );        SELECT  throws_ok(            $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,  
  468. -­‐SELECT  bag_eq(   -­‐        'SELECT  *  FROM

     users',   -­‐        $$  VALUES   -­‐                ('theory',        md5('foo'),    NOW()),   -­‐                ('strongrrl',  md5('w00t'),  NOW())   -­‐        $$,   -­‐        'Both  users  should  be  present'   -­‐);   +SELECT  is(COUNT(*)::INT,  2,  'There  should  be  two  users')   +    FROM  flipr.users;   +   +SELECT  ok(  EXISTS(   +        SELECT  1  FROM  flipr.users   +          WHERE  nickname  =  'strongrrl'   +              AND  password  =  crypt('w00t',  password)   +),  'The  second  user  should  have  been  inserted'  );        SELECT  throws_ok(            $$  SELECT  insert_user('theory',  'ha-­‐ha')  $$,  
  469. @@  -­‐56,14  +56,8  @@  SELECT  throws_ok(        

       'Should  get  an  error  for  duplicate  nickname'    );       -­‐SELECT  bag_eq(   -­‐        'SELECT  *  FROM  users',   -­‐        $$  VALUES   -­‐                ('theory',        md5('foo'),    NOW()),   -­‐                ('strongrrl',  md5('w00t'),  NOW())   -­‐        $$,   -­‐        'Should  still  have  just  the  two  users'   -­‐);   +SELECT  is(COUNT(*)::INT,  2,  'Should  still  have  two  users')   +    FROM  flipr.users;        SELECT  finish();    ROLLBACK;   >
  470. @@  -­‐56,14  +56,8  @@  SELECT  throws_ok(        

       'Should  get  an  error  for  duplicate  nickname'    );       -­‐SELECT  bag_eq(   -­‐        'SELECT  *  FROM  users',   -­‐        $$  VALUES   -­‐                ('theory',        md5('foo'),    NOW()),   -­‐                ('strongrrl',  md5('w00t'),  NOW())   -­‐        $$,   -­‐        'Should  still  have  just  the  two  users'   -­‐);   +SELECT  is(COUNT(*)::INT,  2,  'Should  still  have  two  users')   +    FROM  flipr.users;        SELECT  finish();    ROLLBACK;   >
  471. @@  -­‐56,14  +56,8  @@  SELECT  throws_ok(        

       'Should  get  an  error  for  duplicate  nickname'    );       -­‐SELECT  bag_eq(   -­‐        'SELECT  *  FROM  users',   -­‐        $$  VALUES   -­‐                ('theory',        md5('foo'),    NOW()),   -­‐                ('strongrrl',  md5('w00t'),  NOW())   -­‐        $$,   -­‐        'Should  still  have  just  the  two  users'   -­‐);   +SELECT  is(COUNT(*)::INT,  2,  'Should  still  have  two  users')   +    FROM  flipr.users;        SELECT  finish();    ROLLBACK;   >
  472. FAIL >  pg_prove  -­‐d  flipr_test  test/insert_user.sql #  Failed  test  7:

     "The  user  should  have  been  inserted" #  Failed  test  10:  "The  second  user  should  have  been  inserted" #  Looks  like  you  failed  2  tests  of  12 test/insert_user.sql  ..  Failed  2/12  subtests   Test  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/insert_user.sql  (Wstat:  0  Tests:  12  Failed:  2)    Failed  tests:    7,  10 Files=1,  Tests=12,    0  wallclock  secs Result:  FAIL >
  473. FAIL >  pg_prove  -­‐d  flipr_test  test/insert_user.sql #  Failed  test  7:

     "The  user  should  have  been  inserted" #  Failed  test  10:  "The  second  user  should  have  been  inserted" #  Looks  like  you  failed  2  tests  of  12 test/insert_user.sql  ..  Failed  2/12  subtests   Test  Summary  Report -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ test/insert_user.sql  (Wstat:  0  Tests:  12  Failed:  2)    Failed  tests:    7,  10 Files=1,  Tests=12,    0  wallclock  secs Result:  FAIL > As expected.
  474. Consider this Change    git  diff  deploy   diff  -­‐-­‐git

     a/deploy/insert_user.sql  b/deploy/insert_user.sql index  eb30fed..5c28d02  100644 -­‐-­‐-­‐  a/deploy/insert_user.sql +++  b/deploy/insert_user.sql @@  -­‐8,7  +8,7  @@  CREATE  OR  REPLACE  FUNCTION  flipr.insert_user(          nickname  TEXT,          password  TEXT  )  RETURNS  VOID  LANGUAGE  SQL  SECURITY  DEFINER  AS  $$ -­‐        INSERT  INTO  flipr.users  VALUES($1,  md5($2)); +        INSERT  INTO  flipr.users  values($1,  crypt($2,  gen_salt('md5')));  $$;    COMMIT; > >
  475. Consider this Change    git  diff  deploy   diff  -­‐-­‐git

     a/deploy/insert_user.sql  b/deploy/insert_user.sql index  eb30fed..5c28d02  100644 -­‐-­‐-­‐  a/deploy/insert_user.sql +++  b/deploy/insert_user.sql @@  -­‐8,7  +8,7  @@  CREATE  OR  REPLACE  FUNCTION  flipr.insert_user(          nickname  TEXT,          password  TEXT  )  RETURNS  VOID  LANGUAGE  SQL  SECURITY  DEFINER  AS  $$ -­‐        INSERT  INTO  flipr.users  VALUES($1,  md5($2)); +        INSERT  INTO  flipr.users  values($1,  crypt($2,  gen_salt('md5')));  $$;    COMMIT; > > Simple, right?
  476. Not So Much Copy insert_user.sql to new deploy file Change

    that new file Copy insert_user.sql to new revert file antisocial network
  477. Not So Much Copy insert_user.sql to new deploy file Change

    that new file Copy insert_user.sql to new revert file Test it antisocial network
  478. Not So Much Copy insert_user.sql to new deploy file Change

    that new file Copy insert_user.sql to new revert file Test it Do the same for change_pass.sql antisocial network
  479. Not So Much Copy insert_user.sql to new deploy file Change

    that new file Copy insert_user.sql to new revert file Test it Do the same for change_pass.sql The problem with that… antisocial network
  480. >

  481. >  git  diff  HEAD^ diff  -­‐-­‐git  a/deploy/insert_user_crypt.sql  b/deploy/insert_user_crypto.sql new  file

     mode  100644 index  0000000..fa8d0c6 -­‐-­‐-­‐  /dev/null +++  b/deploy/insert_user_crypt.sql @@  -­‐0,0  +1,8  @@ +-­‐-­‐  requires:  users,  appuser,  pgcrypto + +CREATE  OR  REPLACE  FUNCTION  insert_user( +        nickname  TEXT, +        password  TEXT +)  RETURNS  VOID  LANGUAGE  SQL  AS  $$ +        INSERT  INTO  users  values($1,  crypt($2,  gen_salt('md5'))); +$$; diff  -­‐-­‐git  a/revert/insert_user_crypt.sql  b/revert/insert_user_crypto.sql new  file  mode  100644 index  0000000..a7f4e31 -­‐-­‐-­‐  /dev/null +++  b/revert/insert_user_crypt.sql @@  -­‐0,0  +1,8  @@ +-­‐-­‐  requires:  users,  appuser + +CREATE  OR  REPLACE  FUNCTION  insert_user( +        nickname  TEXT, +        password  TEXT +)  RETURNS  VOID  LANGUAGE  SQL  AS  $$ +        INSERT  INTO  users  values($1,  md5($2)); +$$; >
  482. >  git  diff  HEAD^ diff  -­‐-­‐git  a/deploy/insert_user_crypt.sql  b/deploy/insert_user_crypto.sql new  file

     mode  100644 index  0000000..fa8d0c6 -­‐-­‐-­‐  /dev/null +++  b/deploy/insert_user_crypt.sql @@  -­‐0,0  +1,8  @@ +-­‐-­‐  requires:  users,  appuser,  pgcrypto + +CREATE  OR  REPLACE  FUNCTION  insert_user( +        nickname  TEXT, +        password  TEXT +)  RETURNS  VOID  LANGUAGE  SQL  AS  $$ +        INSERT  INTO  users  values($1,  crypt($2,  gen_salt('md5'))); +$$; diff  -­‐-­‐git  a/revert/insert_user_crypt.sql  b/revert/insert_user_crypto.sql new  file  mode  100644 index  0000000..a7f4e31 -­‐-­‐-­‐  /dev/null +++  b/revert/insert_user_crypt.sql @@  -­‐0,0  +1,8  @@ +-­‐-­‐  requires:  users,  appuser + +CREATE  OR  REPLACE  FUNCTION  insert_user( +        nickname  TEXT, +        password  TEXT +)  RETURNS  VOID  LANGUAGE  SQL  AS  $$ +        INSERT  INTO  users  values($1,  md5($2)); +$$; > Oy.
  483.    sqitch  rework  insert_user  -­‐r  pgcrypto  \    -­‐n  'Changes

     insert_user  to  use  pgcrypto.' Added  "insert_user  [[email protected]­‐r1  pgcrypto]"  to   sqitch.plan. Modify  these  files  as  appropriate:    *  deploy/insert_user.sql    *  revert/insert_user.sql    *  verify/insert_user.sql > Rework It >
  484.    sqitch  rework  insert_user  -­‐r  pgcrypto  \    -­‐n  'Changes

     insert_user  to  use  pgcrypto.' Added  "insert_user  [[email protected]­‐r1  pgcrypto]"  to   sqitch.plan. Modify  these  files  as  appropriate:    *  deploy/insert_user.sql    *  revert/insert_user.sql    *  verify/insert_user.sql > Rework It >
  485.    sqitch  rework  insert_user  -­‐r  pgcrypto  \    -­‐n  'Changes

     insert_user  to  use  pgcrypto.' Added  "insert_user  [[email protected]­‐r1  pgcrypto]"  to   sqitch.plan. Modify  these  files  as  appropriate:    *  deploy/insert_user.sql    *  revert/insert_user.sql    *  verify/insert_user.sql > Rework It >
  486.    sqitch  rework  insert_user  -­‐r  pgcrypto  \    -­‐n  'Changes

     insert_user  to  use  pgcrypto.' Added  "insert_user  [[email protected]­‐r1  pgcrypto]"  to   sqitch.plan. Modify  these  files  as  appropriate:    *  deploy/insert_user.sql    *  revert/insert_user.sql    *  verify/insert_user.sql > Rework It > Same files?
  487.    git  status #  On  branch  master #  Changes  not

     staged  for  commit: #      (use  "git  add  <file>..."  to  update  what  will  be  committe #      (use  "git  checkout  -­‐-­‐  <file>..."  to  discard  changes  in  w # #   modified:      revert/insert_user.sql #   modified:      sqitch.plan #   modified:      test/insert_user.sql # #  Untracked  files: #      (use  "git  add  <file>..."  to  include  in  what  will  be  comm # #   deploy/[email protected]­‐r1.sql #   revert/[email protected]­‐r1.sql #   verify/[email protected]­‐r1.sql no  changes  added  to  commit  (use  "git  add"  and/or  "git  commit > Same Files? >
  488.    git  status #  On  branch  master #  Changes  not

     staged  for  commit: #      (use  "git  add  <file>..."  to  update  what  will  be  committe #      (use  "git  checkout  -­‐-­‐  <file>..."  to  discard  changes  in  w # #   modified:      revert/insert_user.sql #   modified:      sqitch.plan #   modified:      test/insert_user.sql # #  Untracked  files: #      (use  "git  add  <file>..."  to  include  in  what  will  be  comm # #   deploy/[email protected]­‐r1.sql #   revert/[email protected]­‐r1.sql #   verify/[email protected]­‐r1.sql no  changes  added  to  commit  (use  "git  add"  and/or  "git  commit > Same Files? >
  489.    git  status #  On  branch  master #  Changes  not

     staged  for  commit: #      (use  "git  add  <file>..."  to  update  what  will  be  committe #      (use  "git  checkout  -­‐-­‐  <file>..."  to  discard  changes  in  w # #   modified:      revert/insert_user.sql #   modified:      sqitch.plan #   modified:      test/insert_user.sql # #  Untracked  files: #      (use  "git  add  <file>..."  to  include  in  what  will  be  comm # #   deploy/[email protected]­‐r1.sql #   revert/[email protected]­‐r1.sql #   verify/[email protected]­‐r1.sql no  changes  added  to  commit  (use  "git  add"  and/or  "git  commit > Same Files? > As of @v1.0.0-r1
  490.    git  status #  On  branch  master #  Changes  not

     staged  for  commit: #      (use  "git  add  <file>..."  to  update  what  will  be  committe #      (use  "git  checkout  -­‐-­‐  <file>..."  to  discard  changes  in  w # #   modified:      revert/insert_user.sql #   modified:      sqitch.plan #   modified:      test/insert_user.sql # #  Untracked  files: #      (use  "git  add  <file>..."  to  include  in  what  will  be  comm # #   deploy/[email protected]­‐r1.sql #   revert/[email protected]­‐r1.sql #   verify/[email protected]­‐r1.sql no  changes  added  to  commit  (use  "git  add"  and/or  "git  commit > Same Files? > Previous deploy becomes revert
  491. What’s the Diff?    git  diff  deploy/insert_user.sql diff  -­‐-­‐git  a/deploy/insert_user.sql

     b/deploy/insert_user.sql @@  -­‐1,6  +1,7  @@  -­‐-­‐  Deploy  insert_user  -­‐-­‐  requires:  users  -­‐-­‐  requires:  appschema +-­‐-­‐  requires:  pgcrypto    BEGIN;   @@  -­‐8,7  +9,7  @@  CREATE  OR  REPLACE  FUNCTION  flipr.insert_user(          nickname  TEXT,          password  TEXT  )  RETURNS  VOID  LANGUAGE  SQL  SECURITY  DEFINER  AS  $$ -­‐        INSERT  INTO  flipr.users  VALUES($1,  md5($2)); +        INSERT  INTO  flipr.users  values($1,  crypt($2,  gen_salt('md5')));  $$;    COMMIT; >
  492. >  psql  -­‐d  flipr_test  -­‐c  "        DELETE

     FROM  flipr.users;        SELECT  flipr.insert_user('foo',  'secr3t'),                      flipr.insert_user('bar',  'secr3t');        SELECT  nickname,  password  FROM  flipr.users; "  nickname  |                            password                             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  foo            |  $1$nKO47p03$YRXYTt4NoNncTThLyxzEq1  bar            |  $1$LbVUs/p.$LVbvPlkD8rJlixW2nS3WP0 (2  rows) > Send it Up! >  sqitch  deploy Deploying  changes  to  flipr_test    +  insert_user  ..  ok > >
  493. >  psql  -­‐d  flipr_test  -­‐c  "        DELETE

     FROM  flipr.users;        SELECT  flipr.insert_user('foo',  'secr3t'),                      flipr.insert_user('bar',  'secr3t');        SELECT  nickname,  password  FROM  flipr.users; "  nickname  |                            password                             -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  foo            |  $1$nKO47p03$YRXYTt4NoNncTThLyxzEq1  bar            |  $1$LbVUs/p.$LVbvPlkD8rJlixW2nS3WP0 (2  rows) > Send it Up! >  sqitch  deploy Deploying  changes  to  flipr_test    +  insert_user  ..  ok > > \o/
  494.    psql  -­‐d  flipr_test  -­‐c  "        DELETE

     FROM  flipr.users;        SELECT  flipr.insert_user('foo',  'secr3t'),                      flipr.insert_user('bar',  'secr3t');        SELECT  nickname,  password  FROM  flipr.users; "  nickname  |                          password                           -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐+-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐  foo            |  9695da4dd567a19f9b92065f240c6725  bar            |  9695da4dd567a19f9b92065f240c6725 (2  rows) Can We Go Back? >  sqitch  revert  -­‐-­‐to  @HEAD^  -­‐y    -­‐  insert_user  ..  ok > >
  495. -­‐-­‐  Verify  insert_user   ! BEGIN; SELECT  has_function_privilege(    

         'flipr.insert_user(text,  text)',          'execute'   ); verify/insert_u verify/insert_user.sql COMMIT;
  496. SELECT  1/COUNT(*)    FROM  pg_catalog.pg_proc  WHERE  proname  =  'insert_user'  

       AND  pg_get_functiondef(oid)              LIKE  $$%crypt($2,  gen_salt('md5'))%$$; -­‐-­‐  Verify  insert_user   ! BEGIN; SELECT  has_function_privilege(          'flipr.insert_user(text,  text)',          'execute'   ); verify/insert_u verify/insert_user.sql COMMIT;
  497. SELECT  1/COUNT(*)    FROM  pg_catalog.pg_proc  WHERE  proname  =  'insert_user'  

       AND  pg_get_functiondef(oid)              LIKE  $$%crypt($2,  gen_salt('md5'))%$$; -­‐-­‐  Verify  insert_user   ! BEGIN; SELECT  has_function_privilege(          'flipr.insert_user(text,  text)',          'execute'   ); verify/insert_u verify/insert_user.sql COMMIT; Yeah, compare source.
  498. >  emacs  verify/insert_user.sql   > Let’s Go! >  sqitch  deploy

    Deploying  changes  to  flipr_test    +  insert_user  ..  ok >
  499. >  emacs  verify/insert_user.sql   > Let’s Go! >  sqitch  deploy

    Deploying  changes  to  flipr_test    +  insert_user  ..  ok > >  psql  -­‐d  flipr_test  -­‐c  'DELETE  FROM  flipr.users' DELETE  2 >
  500. >  emacs  verify/insert_user.sql   > Let’s Go! >  sqitch  deploy

    Deploying  changes  to  flipr_test    +  insert_user  ..  ok > >  psql  -­‐d  flipr_test  -­‐c  'DELETE  FROM  flipr.users' DELETE  2 >    pg_prove  -­‐d  flipr_test  test/insert_user.sql         test/insert_user.sql  ..  ok           All  tests  successful. Files=1,  Tests=12,    0  wallclock  secs Result:  PASS > Shazam!
  501.    git  add  . >  git  commit  -­‐m  'Update  insert_user

     to  use  pgcrypto.' [master  e4edde6]  Update  insert_user  to  use  pgcrypto.  8  files  changed,  64  insertions(+),  24  deletions(-­‐)  create  mode  100644  deploy/[email protected]­‐r1.sql  create  mode  100644  revert/[email protected]­‐r1.sql  create  mode  100644  verify/[email protected]­‐r1.sql > Add, Commit, Push, Go >
  502.    git  add  . >  git  commit  -­‐m  'Update  insert_user

     to  use  pgcrypto.' [master  e4edde6]  Update  insert_user  to  use  pgcrypto.  8  files  changed,  64  insertions(+),  24  deletions(-­‐)  create  mode  100644  deploy/[email protected]­‐r1.sql  create  mode  100644  revert/[email protected]­‐r1.sql  create  mode  100644  verify/[email protected]­‐r1.sql > Add, Commit, Push, Go >  git  push Counting  objects:  17,  done. Delta  compression  using  up  to  4  threads. Compressing  objects:  100%  (10/10),  done. Writing  objects:  100%  (10/10),  1.77  KiB,  done. Total  10  (delta  6),  reused  0  (delta  0) To  https://github.com/theory/agile-­‐flipr.git      9a9f1da..e4edde6    master  -­‐>  master > >
  503. antisocial network Change It Up git reset --hard insert_user2 Rework

    change_pass Use pgcrypo Test first! Bundle, tag, release
  504. antisocial network Change It Up git reset --hard insert_user2 Rework

    change_pass Use pgcrypo Test first! Bundle, tag, release https:/ /github.com/ theory/agile-flipr.git
  505. Antisocial Networking Startup Flipr Heads To The Deadpool by Michael

    Arrington on November 2, 2010 antisocial network I loved this site. Flipr, an online “antisocial networking” site that encouraged users to alienate each other in order to increase their antisocial cred, is shutting down. The startup’s homepage now consists of a letter to Flipr users instructing them to download their “flips” by November 30, at which point nearly all of the service’s features will be taken offline and data deleted. In the letter, Flipr CEO David Wheeler writes that despite ample venture funding and a dedicated team of database developers, the site underestimated people’s willingness to be assholes. This is not something I can relate to, although from what I’ve been told by more polite society, it is indeed the case. Such a shame.
  506. And that you’re able to use the skills you’ve gained

    in your next job. antisocial network RIP
  507. MOAR Git Bisecting Blaming Pull requests Submitting patches Rewriting history

    Log formatting git help --all antisocial network RIP
  508. MOAR pgTAP Testing privileges Mocking interfaces Custom test functions xUnit-style

    testing Tests maintained in functions antisocial network RIP
  509. MOAR pgTAP Testing privileges Mocking interfaces Custom test functions xUnit-style

    testing Tests maintained in functions http:/ /pgtap.org/documentation.html antisocial network RIP
  510. Sqitch Skillz Add changes Specify Dependencies Manage targets Deploy, Verify,

    Revert changes Rebase changes antisocial network RIP
  511. Sqitch Skillz Add changes Specify Dependencies Manage targets Deploy, Verify,

    Revert changes Rebase changes Rework changes antisocial network RIP
  512. Sqitch Skillz Add changes Specify Dependencies Manage targets Deploy, Verify,

    Revert changes Rebase changes Rework changes Changing branches antisocial network RIP
  513. MOAR Sqitch Cross-project dependencies Multiple projects, one database Script templating

    Variables, loops, conditionals Logging antisocial network RIP
  514. MOAR Sqitch Cross-project dependencies Multiple projects, one database Script templating

    Variables, loops, conditionals Logging Formatting output antisocial network RIP
  515. MOAR Sqitch Cross-project dependencies Multiple projects, one database Script templating

    Variables, loops, conditionals Logging Formatting output sqitch help antisocial network RIP
  516. Agile Database Development David E. Wheeler ! ! ! January

    2014 Portland Text: Attribution-Noncommercial-Share Alike 3.0 United States: http:/ /creativecommons.org/licenses/by-nc-sa/3.0/us/ Images licensed independently and © Their respective owners. iovation