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

Agile Database Development

Agile Database Development

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).

David E. Wheeler

May 22, 2013
Tweet

More Decks by David E. Wheeler

Other Decks in Technology

Transcript

  1. Agile Database Development David E. Wheeler PGCon 2013 Ottawa 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 1 Wednesday, May 22, 13
  2. Agile Database Development David E. Wheeler PGCon 2013 Ottawa 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 1 Wednesday, May 22, 13
  3. David E. Wheeler PGCon 2013 Ottawa 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. 1 Wednesday, May 22, 13
  4. David E. Wheeler PGCon 2013 Ottawa License: Attribution-Noncommercial-Share Alike 3.0

    United States: http:/ /creativecommons.org/licenses/by-nc-sa/3.0/us/ 2 Wednesday, May 22, 13
  5. Build My VC-Funded App for Me for Free David E.

    Wheeler PGCon 2013 Ottawa 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. 2 Wednesday, May 22, 13
  6. This is Genius I had this idea Social networking is

    HAWT Has been for waaaay too long 3 Wednesday, May 22, 13
  7. This is Genius I had this idea Social networking is

    HAWT Has been for waaaay too long The backlash is long overdue 3 Wednesday, May 22, 13
  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 3 Wednesday, May 22, 13
  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… 3 Wednesday, May 22, 13
  10. How it Works Microblogging platform Everyone follows you New users

    follow everyone antisocial network 5 Wednesday, May 22, 13
  11. How it Works Microblogging platform Everyone follows you New users

    follow everyone Goal: Alienate your followers antisocial network 5 Wednesday, May 22, 13
  12. How it Works Microblogging platform Everyone follows you New users

    follow everyone Goal: Alienate your followers Get them to unfollow you antisocial network 5 Wednesday, May 22, 13
  13. 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 5 Wednesday, May 22, 13
  14. Your Task Create the database Use agile development to make

    it right antisocial network 6 Wednesday, May 22, 13
  15. Your Task Create the database Use agile development to make

    it right Contribute to VC-funded Corp antisocial network 6 Wednesday, May 22, 13
  16. Your Task Create the database Use agile development to make

    it right Contribute to VC-funded Corp Profit! antisocial network 6 Wednesday, May 22, 13
  17. Your Task Create the database Use agile development to make

    it right Contribute to VC-funded Corp Profit! For VC-funded Corp antisocial network 6 Wednesday, May 22, 13
  18. Job Requirements PostgreSQL http:/ /postgresql.org/download Git http:/ /git-scm.com/ Sqitch http:/

    /sqitch.org/ pgTAP http:/ /pgtap.org/ antisocial network 18 Wednesday, May 22, 13
  19. Job Requirements PostgreSQL http:/ /postgresql.org/download Git http:/ /git-scm.com/ Sqitch http:/

    /sqitch.org/ pgTAP http:/ /pgtap.org/ pg_prove http:/ /metacpan.org/module/pg_prove antisocial network 18 Wednesday, May 22, 13
  20. Create a Remote Create Git project on GitHub Or BitBucket

    Wherever you like 23 Wednesday, May 22, 13
  21. Create a Remote Create Git project on GitHub Or BitBucket

    Wherever you like Record remote URL 23 Wednesday, May 22, 13
  22. Create a Remote Create Git project on GitHub Or BitBucket

    Wherever you like Record remote URL 23 Wednesday, May 22, 13
  23. ""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)"2c4b510]"Initialize"repo,"add"README. 1"file"changed,"1"insertion(+) "create"mode"100644"README.md > 25 Wednesday, May 22, 13
  24. ""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)"2c4b510]"Initialize"repo,"add"README. 1"file"changed,"1"insertion(+) "create"mode"100644"README.md > 25 Wednesday, May 22, 13
  25. ""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)"2c4b510]"Initialize"repo,"add"README. 1"file"changed,"1"insertion(+) "create"mode"100644"README.md > We have a Git repo. 25 Wednesday, May 22, 13
  26. ""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]""""""flips""""""*>"upstream/flips "*"[new"branch]""""""master"""""*>"upstream/master

    "*"[new"branch]""""""userfuncs""*>"upstream/userfuncs "*"[new"branch]""""""users""""""*>"upstream/users "*"[new"tag]"""""""""insert_user"*>"insert_user "*"[new"tag]"""""""""appschema"*>"appschema "*"[new"tag]"""""""""v1.0.0*r1""*>"v1.0.0*r1 "*"[new"tag]"""""""""reltag"""""*>"reltag "*"[new"tag]"""""""""insert_user2"*>"insert_user2 > 27 Wednesday, May 22, 13
  27. ""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]""""""flips""""""*>"upstream/flips "*"[new"branch]""""""master"""""*>"upstream/master

    "*"[new"branch]""""""userfuncs""*>"upstream/userfuncs "*"[new"branch]""""""users""""""*>"upstream/users "*"[new"tag]"""""""""insert_user"*>"insert_user "*"[new"tag]"""""""""appschema"*>"appschema "*"[new"tag]"""""""""v1.0.0*r1""*>"v1.0.0*r1 "*"[new"tag]"""""""""reltag"""""*>"reltag "*"[new"tag]"""""""""insert_user2"*>"insert_user2 > For resetting later 27 Wednesday, May 22, 13
  28. Git? Manage tree of files over time Distributed development Commit

    changes locally antisocial network 28 Wednesday, May 22, 13
  29. Git? Manage tree of files over time Distributed development Commit

    changes locally Merge to remote (origin, upstream) antisocial network 28 Wednesday, May 22, 13
  30. Git? Manage tree of files over time Distributed development Commit

    changes locally Merge to remote (origin, upstream) Speedy, responsive antisocial network 28 Wednesday, May 22, 13
  31. Git? Manage tree of files over time Distributed development Commit

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

    antisocial network 29 Wednesday, May 22, 13
  33. Why Git? Anyone can clone Complete repository copy Cheap branching

    Make and test local changes antisocial network 29 Wednesday, May 22, 13
  34. Why Git? Anyone can clone Complete repository copy Cheap branching

    Make and test local changes Submit patches, pull requests antisocial network 29 Wednesday, May 22, 13
  35. Why Git? Anyone can clone Complete repository copy Cheap branching

    Make and test local changes Submit patches, pull requests Pull from upstream antisocial network 29 Wednesday, May 22, 13
  36. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    antisocial network 30 Wednesday, May 22, 13
  37. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: antisocial network 30 Wednesday, May 22, 13
  38. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID antisocial network 30 Wednesday, May 22, 13
  39. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID parent ID antisocial network 30 Wednesday, May 22, 13
  40. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID parent ID author antisocial network 30 Wednesday, May 22, 13
  41. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID parent ID author committer antisocial network 30 Wednesday, May 22, 13
  42. PiSHA1 SHA1 ID for every object commit, tag, tree, blob

    Hashed commit text includes: tree ID parent ID author committer message antisocial network 30 Wednesday, May 22, 13
  43. SHAzam! Each commit (except first) includes parent Can trace from

    any commit to the beginning antisocial network 32 Wednesday, May 22, 13
  44. SHAzam! Each commit (except first) includes parent Can trace from

    any commit to the beginning Tampering (corruption) detectable antisocial network 32 Wednesday, May 22, 13
  45. 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 32 Wednesday, May 22, 13
  46. 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 32 Wednesday, May 22, 13
  47. antisocial network Your Turn Configure Git Initialize repository Add origin

    remote Add upstream remote 33 Wednesday, May 22, 13
  48. antisocial network Your Turn Configure Git Initialize repository Add origin

    remote Add upstream remote https:/ /github.com/ theory/agile-flipr.git 33 Wednesday, May 22, 13
  49. 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"] " #"client"="/usr/local/pgsql/bin/psql " #"username"=" " #"password"=" " #"db_name"=" " #"host"=" " #"port"=" " #"sqitch_schema"="sqitch 37 Wednesday, May 22, 13
  50. 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"] " #"client"="/usr/local/pgsql/bin/psql " #"username"=" " #"password"=" " #"db_name"=" " #"host"=" " #"port"=" " #"sqitch_schema"="sqitch --engine pg 37 Wednesday, May 22, 13
  51. Make It So > ""git"add". >"git"commit"*m"'Initialize"Sqitch"configuration.' [master"297c07d]"Initialize"Sqitch"configuration. "2"files"changed,"20"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),"564"bytes,"done. Total"4"(delta"0),"reused"0"(delta"0) To"../flipr*remote """7d28be0..297c07d""master"*>"master > 40 Wednesday, May 22, 13
  52. Make It So > ""git"add". >"git"commit"*m"'Initialize"Sqitch"configuration.' [master"297c07d]"Initialize"Sqitch"configuration. "2"files"changed,"20"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),"564"bytes,"done. Total"4"(delta"0),"reused"0"(delta"0) To"../flipr*remote """7d28be0..297c07d""master"*>"master > 40 Wednesday, May 22, 13
  53. Make It So > ""git"add". >"git"commit"*m"'Initialize"Sqitch"configuration.' [master"297c07d]"Initialize"Sqitch"configuration. "2"files"changed,"20"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),"564"bytes,"done. Total"4"(delta"0),"reused"0"(delta"0) To"../flipr*remote """7d28be0..297c07d""master"*>"master > 40 Wednesday, May 22, 13
  54. ""createdb"flipr_test >"sqitch"**db*name"flipr_test"deploy Adding"metadata"tables"to"flipr_test Deploying"to"flipr_test ""+"appschema".."ok > Make it So! >

    ""psql"*d"flipr_test"*c"'\dn"flipr' List"of"schemas "Name""|"Owner" *******+******* "flipr"|"david > 46 Wednesday, May 22, 13
  55. ""createdb"flipr_test >"sqitch"**db*name"flipr_test"deploy Adding"metadata"tables"to"flipr_test Deploying"to"flipr_test ""+"appschema".."ok > Make it So! >

    ""psql"*d"flipr_test"*c"'\dn"flipr' List"of"schemas "Name""|"Owner" *******+******* "flipr"|"david > Trust, but verify 46 Wednesday, May 22, 13
  56. ""createdb"flipr_test >"sqitch"**db*name"flipr_test"deploy Adding"metadata"tables"to"flipr_test Deploying"to"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 46 Wednesday, May 22, 13
  57. Trust, But Verify > ""emacs"verify/appschema.sql > ""sqitch"**db*name"flipr_test"verify Verifying"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 > 48 Wednesday, May 22, 13
  58. Trust, But Verify > ""emacs"verify/appschema.sql > ""sqitch"**db*name"flipr_test"verify Verifying"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 > 48 Wednesday, May 22, 13
  59. Trust, But Verify > ""emacs"verify/appschema.sql > ""sqitch"**db*name"flipr_test"verify Verifying"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 > 48 Wednesday, May 22, 13
  60. Trust, But Verify > ""emacs"verify/appschema.sql > ""sqitch"**db*name"flipr_test"verify Verifying"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 > 48 Wednesday, May 22, 13
  61. Commit It! > ""git"add". >"git"commit"*m"'Add"flipr"schema.' [master"dc23038]"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),"943"bytes,"done. Total"9"(delta"0),"reused"0"(delta"0) To"../flipr*remote """a4f765f..dc23038""master"*>"master 53 Wednesday, May 22, 13
  62. Commit It! > ""git"add". >"git"commit"*m"'Add"flipr"schema.' [master"dc23038]"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),"943"bytes,"done. Total"9"(delta"0),"reused"0"(delta"0) To"../flipr*remote """a4f765f..dc23038""master"*>"master 53 Wednesday, May 22, 13
  63. Save My Fingers > ""sqitch"config"core.pg.db_name"flipr_test > ""sqitch"status #"On"database"flipr_test #"Project:""flipr #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3

    #"Name:"""""appschema #"Deployed:"2013*05*21"15:10:02"*0400 #"By:"""""""David"E."Wheeler"<[email protected]> #" Nothing"to"deploy"(up*to*date) > No --db-name 56 Wednesday, May 22, 13
  64. Save My Fingers > ""sqitch"config"core.pg.db_name"flipr_test > ""sqitch"status #"On"database"flipr_test #"Project:""flipr #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3

    #"Name:"""""appschema #"Deployed:"2013*05*21"15:10:02"*0400 #"By:"""""""David"E."Wheeler"<[email protected]> #" Nothing"to"deploy"(up*to*date) > No --db-name >"sqitch"config"**bool"deploy.verify"true >"sqitch"config"**bool"rebase.verify"true > Always verify. 56 Wednesday, May 22, 13
  65. 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),"376"bytes,"done. Total"3"(delta"2),"reused"0"(delta"0) To"../flipr*remote

    """dc23038..795697f""master"*>"master > ""git"add". >"git"commit"*m"'Add"db_name"and"verify"to"config.' [master"795697f]"Add"db_name"and"verify"to"config. "1"file"changed,"6"insertions(+) > 57 Wednesday, May 22, 13
  66. Not Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    difficult to track antisocial network 58 Wednesday, May 22, 13
  67. Not Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    difficult to track No VCS awareness antisocial network 58 Wednesday, May 22, 13
  68. SQL Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    hard to track No VCS awareness antisocial network 59 Wednesday, May 22, 13
  69. SQL Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    hard to track No VCS awareness ——————————————— antisocial network 59 Wednesday, May 22, 13
  70. SQL Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    hard to track No VCS awareness ——————————————— ——————————————————— antisocial network 59 Wednesday, May 22, 13
  71. SQL Migrations? Incomplete mini-language No logical replication integration Numbered scripts

    hard to track No VCS awareness ——————————————— ——————————————————— Managing procedures is a PITA antisocial network 59 Wednesday, May 22, 13
  72. Sqitch Philosophy No opinions Native scripting (psql, sqlite3, SQL*Plus) Cross-project

    dependency resolution antisocial network 61 Wednesday, May 22, 13
  73. Sqitch Philosophy No opinions Native scripting (psql, sqlite3, SQL*Plus) Cross-project

    dependency resolution Distribution bundling antisocial network 61 Wednesday, May 22, 13
  74. Sqitch Philosophy No opinions Native scripting (psql, sqlite3, SQL*Plus) Cross-project

    dependency resolution Distribution bundling Integrated verification testing antisocial network 61 Wednesday, May 22, 13
  75. Sqitch Philosophy No opinions Native scripting (psql, sqlite3, SQL*Plus) Cross-project

    dependency resolution Distribution bundling Integrated verification testing No numbering antisocial network 61 Wednesday, May 22, 13
  76. 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 61 Wednesday, May 22, 13
  77. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify antisocial network 62 Wednesday, May 22, 13
  78. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: antisocial network 62 Wednesday, May 22, 13
  79. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: Project antisocial network 62 Wednesday, May 22, 13
  80. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: Project Name antisocial network 62 Wednesday, May 22, 13
  81. SHAzbat SHA1 ID for every object Stolen from Git change,

    tag, deploy, revert, verify Hashed change text includes: Project Name Parent ID antisocial network 62 Wednesday, May 22, 13
  82. 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 62 Wednesday, May 22, 13
  83. SHApay! Each change (except first) includes parent Can trace from

    any change to the beginning antisocial network 64 Wednesday, May 22, 13
  84. SHApay! Each change (except first) includes parent Can trace from

    any change to the beginning Change tampering (corruption) detectable antisocial network 64 Wednesday, May 22, 13
  85. 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 64 Wednesday, May 22, 13
  86. 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 64 Wednesday, May 22, 13
  87. Sqitch Features Reduced duplication Built-in configuration Iterative development Deployment planning

    Git-style interface Deployment tagging antisocial network 65 Wednesday, May 22, 13
  88. antisocial network Your Turn Configure Sqitch Initialize project Add appschema

    change Deploy/Revert Commit/push 66 Wednesday, May 22, 13
  89. antisocial network Your Turn Configure Sqitch Initialize project Add appschema

    change Deploy/Revert Commit/push https:/ /github.com/ theory/agile-flipr.git 66 Wednesday, May 22, 13
  90. > 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 69 Wednesday, May 22, 13
  91. > 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 69 Wednesday, May 22, 13
  92. > 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 69 Wednesday, May 22, 13
  93. ""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 70 Wednesday, May 22, 13
  94. ""git"add". > ""git"commit"*m"'Add"appschema"test.' [master"e46bdf9]"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),"487"bytes,"done. Total"4"(delta"1),"reused"0"(delta"0) To"../flipr*remote """795697f..e46bdf9""master"*>"master > 71 Wednesday, May 22, 13
  95. OMG TAP WTF? The Test Anything Protocol (TAP) is a

    protocol to allow communication between unit tests and a test harness. It allows individual tests (TAP producers) to communicate test results to the testing harness in a language-agnostic way. —Wikipedia 72 Wednesday, May 22, 13
  96. What does that mean in practice? Test output easy to

    interpret What is TAP? antisocial network 73 Wednesday, May 22, 13
  97. What does that mean in practice? Test output easy to

    interpret By humans What is TAP? antisocial network 73 Wednesday, May 22, 13
  98. What does that mean in practice? Test output easy to

    interpret By humans By computers What is TAP? antisocial network 73 Wednesday, May 22, 13
  99. What does that mean in practice? Test output easy to

    interpret By humans By computers pg_prove (the harness) What is TAP? antisocial network 73 Wednesday, May 22, 13
  100. 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 73 Wednesday, May 22, 13
  101. 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 73 Wednesday, May 22, 13
  102. What’s the plan, man? Includes Test controls: plan() — How

    many tests? antisocial network 74 Wednesday, May 22, 13
  103. What’s the plan, man? Includes Test controls: plan() — How

    many tests? no_plan() — Unknown number of tests antisocial network 74 Wednesday, May 22, 13
  104. What’s the plan, man? Includes Test controls: plan() — How

    many tests? no_plan() — Unknown number of tests diag() — Diagnostic output antisocial network 74 Wednesday, May 22, 13
  105. 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 74 Wednesday, May 22, 13
  106. Scalarly Includes simple scalar test functions: ok() — Boolean is()

    — Value comparison antisocial network 75 Wednesday, May 22, 13
  107. Scalarly Includes simple scalar test functions: ok() — Boolean is()

    — Value comparison isnt() — NOT is() antisocial network 75 Wednesday, May 22, 13
  108. Scalarly Includes simple scalar test functions: ok() — Boolean is()

    — Value comparison isnt() — NOT is() cmp_ok() — Compare with specific operator antisocial network 75 Wednesday, May 22, 13
  109. 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 75 Wednesday, May 22, 13
  110. 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 75 Wednesday, May 22, 13
  111. It’s All Relative Includes functions for testing relations: results_eq() —

    Ordered results antisocial network 76 Wednesday, May 22, 13
  112. It’s All Relative Includes functions for testing relations: results_eq() —

    Ordered results set_eq() — Set of values antisocial network 76 Wednesday, May 22, 13
  113. 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 76 Wednesday, May 22, 13
  114. 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 76 Wednesday, May 22, 13
  115. 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 76 Wednesday, May 22, 13
  116. I’m Okay, You’re Okay throws_ok() — Throws an exception throws_like()

    — Exception matches regex antisocial network 77 Wednesday, May 22, 13
  117. I’m Okay, You’re Okay throws_ok() — Throws an exception throws_like()

    — Exception matches regex skip() — Skip a subset of tests antisocial network 77 Wednesday, May 22, 13
  118. 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 77 Wednesday, May 22, 13
  119. Schematics has_table(), has_view(), has_function(), etc. columns_are(), has_pk(), fk_ok(), etc. col_type_is(),

    col_not_null(), col_default_is() antisocial network 78 Wednesday, May 22, 13
  120. 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 78 Wednesday, May 22, 13
  121. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests antisocial network 79 Wednesday, May 22, 13
  122. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress antisocial network 79 Wednesday, May 22, 13
  123. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions antisocial network 79 Wednesday, May 22, 13
  124. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions Roles and privileges assertions antisocial network 79 Wednesday, May 22, 13
  125. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions Roles and privileges assertions http:/ /pgtap.org/ antisocial network 79 Wednesday, May 22, 13
  126. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions Roles and privileges assertions http:/ /pgtap.org/ http:/ /pgxn.org/extension/pgtap/ antisocial network 79 Wednesday, May 22, 13
  127. Other Features and Topics xUnit-Style testing Test-Driven development Integration with

    Perl unit tests Integration with pg_regress Negative assertions Roles and privileges assertions http:/ /pgtap.org/ http:/ /pgxn.org/extension/pgtap/ antisocial network 79 Wednesday, May 22, 13
  128. antisocial network Let’s do it! Create appschema test Use pgTAP

    Run test with pg_prove 80 Wednesday, May 22, 13
  129. antisocial network Let’s do it! Create appschema test Use pgTAP

    Run test with pg_prove Make it fail 80 Wednesday, May 22, 13
  130. antisocial network Let’s do it! Create appschema test Use pgTAP

    Run test with pg_prove Make it fail Make it pass! 80 Wednesday, May 22, 13
  131. antisocial network Let’s do it! Create appschema test Use pgTAP

    Run test with pg_prove Make it fail Make it pass! Commit/Push 80 Wednesday, May 22, 13
  132. 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 80 Wednesday, May 22, 13
  133. “The act of writing a unit test is more an

    act of design than of verification. It is also more an act of documentation than of verification. The act of writing a unit test closes a remarkable number of feedback loops, the least of which is the one per– taining to the verification of function.” —Robert C. Martin 83 Wednesday, May 22, 13
  134. Database Design Specify requirements Implement schema design Program applications QA

    Big jump! antisocial network 88 Wednesday, May 22, 13
  135. Database Design Specify requirements Implement schema design Program applications QA

    Pricy to fix antisocial network 88 Wednesday, May 22, 13
  136. Database Design Specify requirements Implement schema design Program applications QA

    Never mind bureaucracy of DBA department antisocial network 88 Wednesday, May 22, 13
  137. Why TDDD Ensure data quality Data is a core asset

    antisocial network 89 Wednesday, May 22, 13
  138. Why TDDD Ensure data quality Data is a core asset

    Validate business rules antisocial network 89 Wednesday, May 22, 13
  139. Why TDDD Ensure data quality Data is a core asset

    Validate business rules Stored procedures antisocial network 89 Wednesday, May 22, 13
  140. Why TDDD Ensure data quality Data is a core asset

    Validate business rules Stored procedures Triggers antisocial network 89 Wednesday, May 22, 13
  141. Why TDDD Ensure data quality Data is a core asset

    Validate business rules Stored procedures Triggers Views antisocial network 89 Wednesday, May 22, 13
  142. Why TDDD Identify defects early …and often! Create design iteratively

    Evolutionarily antisocial network 90 Wednesday, May 22, 13
  143. Why TDDD Identify defects early …and often! Create design iteratively

    Evolutionarily Validate refactorings antisocial network 90 Wednesday, May 22, 13
  144. Why TDDD Identify defects early …and often! Create design iteratively

    Evolutionarily Validate refactorings Make sure nothing breaks antisocial network 90 Wednesday, May 22, 13
  145. “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?” 92 Wednesday, May 22, 13
  146. “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?” 93 Wednesday, May 22, 13
  147. “Wouldn’t it be nice to have a test suite to

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

    apps antisocial network 97 Wednesday, May 22, 13
  149. TDD How Ideally separate from app tests May be many

    apps DB should present interface to all antisocial network 97 Wednesday, May 22, 13
  150. TDD How Ideally separate from app tests May be many

    apps DB should present interface to all Apps may use different permissions antisocial network 97 Wednesday, May 22, 13
  151. 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 97 Wednesday, May 22, 13
  152. 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 97 Wednesday, May 22, 13
  153. ""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 > 107 Wednesday, May 22, 13
  154. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: antisocial network 116 Wednesday, May 22, 13
  155. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change antisocial network 116 Wednesday, May 22, 13
  156. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change @ROOT First change antisocial network 116 Wednesday, May 22, 13
  157. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change @ROOT First change Two modifiers: antisocial network 116 Wednesday, May 22, 13
  158. Sqitch Tags Start with @ To distinguish from changes Two

    symbolic tags: @HEAD Last change @ROOT First change Two modifiers: ^ Previous change antisocial network 116 Wednesday, May 22, 13
  159. 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 116 Wednesday, May 22, 13
  160. Specifying Changes users Change named “users” @HEAD^ Second to last

    change antisocial network 117 Wednesday, May 22, 13
  161. Specifying Changes users Change named “users” @HEAD^ Second to last

    change users^ Change before users antisocial network 117 Wednesday, May 22, 13
  162. Specifying Changes users Change named “users” @HEAD^ Second to last

    change users^ Change before users @ROOT~ Second change antisocial network 117 Wednesday, May 22, 13
  163. Specifying Changes users Change named “users” @HEAD^ Second to last

    change users^ Change before users @ROOT~ Second change appschema~ Change after appschema antisocial network 117 Wednesday, May 22, 13
  164. 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 117 Wednesday, May 22, 13
  165. 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 users antisocial network 117 Wednesday, May 22, 13
  166. >""sqitch"revert"**to"@HEAD^"*y Reverting"changes"to"appschema"from"flipr_test ""*"users".."ok > ""sqitch"status #"On"database"flipr_test #"Project:""flipr #"Change:"""748346dfe73cf2af32a8b7088fd75ad8d7aecda3 #"Name:"""""appschema #"Deployed:"2013*05*21"15:10:02"*0400

    #"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". > 118 Wednesday, May 22, 13
  167. ""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 > 120 Wednesday, May 22, 13
  168. ""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! 120 Wednesday, May 22, 13
  169. ""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"verify/users.sql 120 Wednesday, May 22, 13
  170. 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"*"FROM"no_plan(); 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; 121 Wednesday, May 22, 13
  171. 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"*"FROM"no_plan(); 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; 121 Wednesday, May 22, 13
  172. >"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 > 122 Wednesday, May 22, 13
  173. >"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. 122 Wednesday, May 22, 13
  174. **"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() 124 Wednesday, May 22, 13
  175. ""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 > 125 Wednesday, May 22, 13
  176. ""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. 125 Wednesday, May 22, 13
  177. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change antisocial network 128 Wednesday, May 22, 13
  178. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change Revise test antisocial network 128 Wednesday, May 22, 13
  179. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change Revise test Revise and rebase change antisocial network 128 Wednesday, May 22, 13
  180. Wash, Rinse, Repeat Add failing simple test Add and deploy

    change Revise test Revise and rebase change Wash, Rinse, Repeat antisocial network 128 Wednesday, May 22, 13
  181. 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 128 Wednesday, May 22, 13
  182. 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 128 Wednesday, May 22, 13
  183. Time to Work! Prepare to hack! git checkout master git

    branch -D users antisocial network 129 Wednesday, May 22, 13
  184. Time to Work! Prepare to hack! git checkout master git

    branch -D users git checkout -b users antisocial network 129 Wednesday, May 22, 13
  185. Time to Work! Prepare to hack! git checkout master git

    branch -D users git checkout -b users git reset --hard upstream/users antisocial network 129 Wednesday, May 22, 13
  186. 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 129 Wednesday, May 22, 13
  187. 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 129 Wednesday, May 22, 13
  188. Caution: Hard Reset Ahead A rare destructive Git command Deletes

    HEAD snapshot Replaces it with new snapshot 130 Wednesday, May 22, 13
  189. Caution: Hard Reset Ahead A rare destructive Git command Deletes

    HEAD snapshot Replaces it with new snapshot Almost un-reversible 130 Wednesday, May 22, 13
  190. 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 130 Wednesday, May 22, 13
  191. 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! 130 Wednesday, May 22, 13
  192. antisocial network Flip Out Create flips branch Create flips table

    flip_id SERIAL PK 131 Wednesday, May 22, 13
  193. antisocial network Flip Out Create flips branch Create flips table

    flip_id SERIAL PK nickname FK 131 Wednesday, May 22, 13
  194. antisocial network Flip Out Create flips branch Create flips table

    flip_id SERIAL PK nickname FK body TEXT 131 Wednesday, May 22, 13
  195. antisocial network Flip Out Create flips branch Create flips table

    flip_id SERIAL PK nickname FK body TEXT timestamptz 131 Wednesday, May 22, 13
  196. antisocial network Flip Out Create flips branch Create flips table

    flip_id SERIAL PK nickname FK body TEXT timestamptz Use TDDD 131 Wednesday, May 22, 13
  197. 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 131 Wednesday, May 22, 13
  198. 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' ); 134 Wednesday, May 22, 13
  199. 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' ); 134 Wednesday, May 22, 13
  200. 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' ); 134 Wednesday, May 22, 13
  201. 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' ); 134 Wednesday, May 22, 13
  202. 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' ); 134 Wednesday, May 22, 13
  203. 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' ); 136 Wednesday, May 22, 13
  204. 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' ); 136 Wednesday, May 22, 13
  205. 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 136 Wednesday, May 22, 13
  206. 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' ); 136 Wednesday, May 22, 13
  207. 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' ); 136 Wednesday, May 22, 13
  208. 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; 137 Wednesday, May 22, 13
  209. Reset, Mes Amis! git checkout users git reset --hard upstream/users

    antisocial network 147 Wednesday, May 22, 13
  210. Reset, Mes Amis! git checkout users git reset --hard upstream/users

    git checkout -b userfuncs antisocial network 147 Wednesday, May 22, 13
  211. Reset, Mes Amis! git checkout users git reset --hard upstream/users

    git checkout -b userfuncs git reset --hard insert_user antisocial network 147 Wednesday, May 22, 13
  212. antisocial network None Shall Pass Create change_pass() Params: nickname old_pass

    new_pass Only update if old pass correct 148 Wednesday, May 22, 13
  213. antisocial network None Shall Pass Create change_pass() Params: nickname old_pass

    new_pass Only update if old pass correct Use TDDD 148 Wednesday, May 22, 13
  214. 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 148 Wednesday, May 22, 13
  215. Branching Out users branched from master flips branched from users

    antisocial network 154 Wednesday, May 22, 13
  216. Branching Out users branched from master flips branched from users

    userfuncs branched from users antisocial network 154 Wednesday, May 22, 13
  217. Branching Out users branched from master flips branched from users

    userfuncs branched from users users and flips merged to master antisocial network 154 Wednesday, May 22, 13
  218. 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 154 Wednesday, May 22, 13
  219. Backbrancher master users A B flips C Add flips table

    branch branch 155 Wednesday, May 22, 13
  220. Backbrancher master users A B flips C userfuncs Add insert_user

    function D branch branch branch 155 Wednesday, May 22, 13
  221. Backbrancher master users A B flips C userfuncs D branch

    branch branch E Add change_pass function 155 Wednesday, May 22, 13
  222. Backbrancher master users A B flips C userfuncs B merge

    B D branch branch branch E 155 Wednesday, May 22, 13
  223. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D branch branch branch E 155 Wednesday, May 22, 13
  224. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D F merge D & E branch branch branch E 155 Wednesday, May 22, 13
  225. 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 155 Wednesday, May 22, 13
  226. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D conflict! branch branch branch E 155 Wednesday, May 22, 13
  227. Backbrancher master users A B flips C userfuncs B merge

    B C merge C D conflict! Now what? branch branch branch E 155 Wednesday, May 22, 13
  228. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch D E 156 Wednesday, May 22, 13
  229. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch Last common with master: B 156 Wednesday, May 22, 13
  230. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch C 156 Wednesday, May 22, 13
  231. Rebase master master users A B flips C userfuncs B

    merge B C merge C branch branch branch C D Rebased E 156 Wednesday, May 22, 13
  232. 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 156 Wednesday, May 22, 13
  233. \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 156 Wednesday, May 22, 13
  234. >"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: """/Users/david/Desktop/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 > 158 Wednesday, May 22, 13
  235. >"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: """/Users/david/Desktop/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. 158 Wednesday, May 22, 13
  236. >"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: """/Users/david/Desktop/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 158 Wednesday, May 22, 13
  237. >"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: """/Users/david/Desktop/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… 158 Wednesday, May 22, 13
  238. >"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: """/Users/david/Desktop/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! 158 Wednesday, May 22, 13
  239. >"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: """/Users/david/Desktop/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 > 158 Wednesday, May 22, 13
  240. Wha Happen? Same conflict Both branches modified sqitch.plan The same

    line! antisocial network 159 Wednesday, May 22, 13
  241. >"git"diff diff"**cc"sqitch.plan index"0d5a515,96b1b7e..0000000 ***"a/sqitch.plan +++"b/sqitch.plan @@@"*4,4"*4,4"+4,8"@@@ "" ""appschema"2013*05*15T06:03:09Z"David"E."Wheeler"<d ""users"[appschema]"2013*05*17T04:59:01Z"David"E."Wh ++<<<<<<<"HEAD

    ""flips"[appschema"users]"2013*05*18T00:04:56Z"David ++======= +"insert_user"[users"appschema]"2013*05*20T20:53:56Z ++>>>>>>>"Add"`insert_user()`. > Wha Happen? > 160 Wednesday, May 22, 13
  242. >"git"diff diff"**cc"sqitch.plan index"0d5a515,96b1b7e..0000000 ***"a/sqitch.plan +++"b/sqitch.plan @@@"*4,4"*4,4"+4,8"@@@ "" ""appschema"2013*05*15T06:03:09Z"David"E."Wheeler"<d ""users"[appschema]"2013*05*17T04:59:01Z"David"E."Wh ++<<<<<<<"HEAD

    ""flips"[appschema"users]"2013*05*18T00:04:56Z"David ++======= +"insert_user"[users"appschema]"2013*05*20T20:53:56Z ++>>>>>>>"Add"`insert_user()`. > Wha Happen? > 160 Wednesday, May 22, 13
  243. >"git"diff diff"**cc"sqitch.plan index"0d5a515,96b1b7e..0000000 ***"a/sqitch.plan +++"b/sqitch.plan @@@"*4,4"*4,4"+4,8"@@@ "" ""appschema"2013*05*15T06:03:09Z"David"E."Wheeler"<d ""users"[appschema]"2013*05*17T04:59:01Z"David"E."Wh ++<<<<<<<"HEAD

    ""flips"[appschema"users]"2013*05*18T00:04:56Z"David ++======= +"insert_user"[users"appschema]"2013*05*20T20:53:56Z ++>>>>>>>"Add"`insert_user()`. > Wha Happen? > B 160 Wednesday, May 22, 13
  244. >"git"diff diff"**cc"sqitch.plan index"0d5a515,96b1b7e..0000000 ***"a/sqitch.plan +++"b/sqitch.plan @@@"*4,4"*4,4"+4,8"@@@ "" ""appschema"2013*05*15T06:03:09Z"David"E."Wheeler"<d ""users"[appschema]"2013*05*17T04:59:01Z"David"E."Wh ++<<<<<<<"HEAD

    ""flips"[appschema"users]"2013*05*18T00:04:56Z"David ++======= +"insert_user"[users"appschema]"2013*05*20T20:53:56Z ++>>>>>>>"Add"`insert_user()`. > Wha Happen? > C B 160 Wednesday, May 22, 13
  245. >"git"diff diff"**cc"sqitch.plan index"0d5a515,96b1b7e..0000000 ***"a/sqitch.plan +++"b/sqitch.plan @@@"*4,4"*4,4"+4,8"@@@ "" ""appschema"2013*05*15T06:03:09Z"David"E."Wheeler"<d ""users"[appschema]"2013*05*17T04:59:01Z"David"E."Wh ++<<<<<<<"HEAD

    ""flips"[appschema"users]"2013*05*18T00:04:56Z"David ++======= +"insert_user"[users"appschema]"2013*05*20T20:53:56Z ++>>>>>>>"Add"`insert_user()`. > Wha Happen? > C D B 160 Wednesday, May 22, 13
  246. Scratch that Sqitch Screwed either way? Fortunately, this is Git

    antisocial network 161 Wednesday, May 22, 13
  247. Scratch that Sqitch Screwed either way? Fortunately, this is Git

    Tell it to treat Sqitch plans differently antisocial network 161 Wednesday, May 22, 13
  248. Scratch that Sqitch Screwed either way? Fortunately, this is Git

    Tell it to treat Sqitch plans differently Changes on single lines antisocial network 161 Wednesday, May 22, 13
  249. 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 161 Wednesday, May 22, 13
  250. 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 161 Wednesday, May 22, 13
  251. 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 162 Wednesday, May 22, 13
  252. Hallelunion Just appends lines Exactly how changes work Let’s clean

    up our mess antisocial network 163 Wednesday, May 22, 13
  253. Hallelunion Just appends lines Exactly how changes work Let’s clean

    up our mess And try again antisocial network 163 Wednesday, May 22, 13
  254. ""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 > 164 Wednesday, May 22, 13
  255. ""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 164 Wednesday, May 22, 13
  256. ""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 164 Wednesday, May 22, 13
  257. ""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 164 Wednesday, May 22, 13
  258. ""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 164 Wednesday, May 22, 13
  259. ""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 164 Wednesday, May 22, 13
  260. ""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! 164 Wednesday, May 22, 13
  261. ""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! 164 Wednesday, May 22, 13
  262. sqitch.plan What’s the Plan, Man? %syntax*version=1.0.0*b2 %project=flipr %uri=file://../flipr*remote appschema"2013*05*21T19:01:04Z"David"E."Wheeler" <[email protected]>"#"Adds"flipr"app"schema.

    users"[appschema]"2013*05*21T19:41:54Z"David"E."Wheeler" <[email protected]>"#"Creates"table"to"track"our" users. flips"[appschema"users]"2013*05*21T20:38:21Z"David"E." Wheeler"<[email protected]>"#"Adds"table"for"storing" flips. insert_user"[users"appschema]"2013*05*21T20:51:05Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to" insert"a"user. change_pass"[users"appschema]"2013*05*21T21:05:42Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to 165 Wednesday, May 22, 13
  263. sqitch.plan What’s the Plan, Man? %syntax*version=1.0.0*b2 %project=flipr %uri=file://../flipr*remote appschema"2013*05*21T19:01:04Z"David"E."Wheeler" <[email protected]>"#"Adds"flipr"app"schema.

    users"[appschema]"2013*05*21T19:41:54Z"David"E."Wheeler" <[email protected]>"#"Creates"table"to"track"our" users. flips"[appschema"users]"2013*05*21T20:38:21Z"David"E." Wheeler"<[email protected]>"#"Adds"table"for"storing" flips. insert_user"[users"appschema]"2013*05*21T20:51:05Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to" insert"a"user. change_pass"[users"appschema]"2013*05*21T21:05:42Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to 165 Wednesday, May 22, 13
  264. sqitch.plan What’s the Plan, Man? %syntax*version=1.0.0*b2 %project=flipr %uri=file://../flipr*remote appschema"2013*05*21T19:01:04Z"David"E."Wheeler" <[email protected]>"#"Adds"flipr"app"schema.

    users"[appschema]"2013*05*21T19:41:54Z"David"E."Wheeler" <[email protected]>"#"Creates"table"to"track"our" users. flips"[appschema"users]"2013*05*21T20:38:21Z"David"E." Wheeler"<[email protected]>"#"Adds"table"for"storing" flips. insert_user"[users"appschema]"2013*05*21T20:51:05Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to" insert"a"user. change_pass"[users"appschema]"2013*05*21T21:05:42Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to 165 Wednesday, May 22, 13
  265. sqitch.plan What’s the Plan, Man? %syntax*version=1.0.0*b2 %project=flipr %uri=file://../flipr*remote appschema"2013*05*21T19:01:04Z"David"E."Wheeler" <[email protected]>"#"Adds"flipr"app"schema.

    users"[appschema]"2013*05*21T19:41:54Z"David"E."Wheeler" <[email protected]>"#"Creates"table"to"track"our" users. flips"[appschema"users]"2013*05*21T20:38:21Z"David"E." Wheeler"<[email protected]>"#"Adds"table"for"storing" flips. insert_user"[users"appschema]"2013*05*21T20:51:05Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to" insert"a"user. change_pass"[users"appschema]"2013*05*21T21:05:42Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to 165 Wednesday, May 22, 13
  266. sqitch.plan What’s the Plan, Man? %syntax*version=1.0.0*b2 %project=flipr %uri=file://../flipr*remote appschema"2013*05*21T19:01:04Z"David"E."Wheeler" <[email protected]>"#"Adds"flipr"app"schema.

    users"[appschema]"2013*05*21T19:41:54Z"David"E."Wheeler" <[email protected]>"#"Creates"table"to"track"our" users. flips"[appschema"users]"2013*05*21T20:38:21Z"David"E." Wheeler"<[email protected]>"#"Adds"table"for"storing" flips. insert_user"[users"appschema]"2013*05*21T20:51:05Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to" insert"a"user. change_pass"[users"appschema]"2013*05*21T21:05:42Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to 165 Wednesday, May 22, 13
  267. sqitch.plan What’s the Plan, Man? %syntax*version=1.0.0*b2 %project=flipr %uri=file://../flipr*remote appschema"2013*05*21T19:01:04Z"David"E."Wheeler" <[email protected]>"#"Adds"flipr"app"schema.

    users"[appschema]"2013*05*21T19:41:54Z"David"E."Wheeler" <[email protected]>"#"Creates"table"to"track"our" users. flips"[appschema"users]"2013*05*21T20:38:21Z"David"E." Wheeler"<[email protected]>"#"Adds"table"for"storing" flips. insert_user"[users"appschema]"2013*05*21T20:51:05Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to" insert"a"user. change_pass"[users"appschema]"2013*05*21T21:05:42Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to 165 Wednesday, May 22, 13
  268. sqitch.plan What’s the Plan, Man? %syntax*version=1.0.0*b2 %project=flipr %uri=file://../flipr*remote appschema"2013*05*21T19:01:04Z"David"E."Wheeler" <[email protected]>"#"Adds"flipr"app"schema.

    users"[appschema]"2013*05*21T19:41:54Z"David"E."Wheeler" <[email protected]>"#"Creates"table"to"track"our" users. flips"[appschema"users]"2013*05*21T20:38:21Z"David"E." Wheeler"<[email protected]>"#"Adds"table"for"storing" flips. insert_user"[users"appschema]"2013*05*21T20:51:05Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to" insert"a"user. change_pass"[users"appschema]"2013*05*21T21:05:42Z"David" E."Wheeler"<[email protected]>"#"Creates"a"function"to Perfect 165 Wednesday, May 22, 13
  269. >"git"add". > ""git"commit"*m"'Use"union"merge"for"`sqitch.plan`.' [userfuncs"0e31da9]"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"../flipr*remote "+"e7477c...0e31da"userfuncs"*>"userfuncs"(forced"update > Make it So > Overwrite history 167 Wednesday, May 22, 13
  270. >"git"add". > ""git"commit"*m"'Use"union"merge"for"`sqitch.plan`.' [userfuncs"0e31da9]"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"../flipr*remote "+"e7477c...0e31da"userfuncs"*>"userfuncs"(forced"update > Make it So > 167 Wednesday, May 22, 13
  271. ""git"merge"userfuncs Updating"fbf8469..0e31da9 Fast*forward ".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' > > 168 Wednesday, May 22, 13
  272. ""git"merge"userfuncs Updating"fbf8469..0e31da9 Fast*forward ".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/ 168 Wednesday, May 22, 13
  273. Ship Shape Good work so far People gonna flip out

    antisocial network 171 Wednesday, May 22, 13
  274. Ship Shape Good work so far People gonna flip out

    Let’s tag a dev release antisocial network 171 Wednesday, May 22, 13
  275. Ship Shape Good work so far People gonna flip out

    Let’s tag a dev release Bundle it up antisocial network 171 Wednesday, May 22, 13
  276. Ship Shape Good work so far People gonna flip out

    Let’s tag a dev release Bundle it up And ship it antisocial network 171 Wednesday, May 22, 13
  277. ""git"tag"v1.0.0*r1"*am"'Tag"v1.0.0*r1.' > ""sqitch"tag"v1.0.0*r1"*n"'Tag"v1.0.0*r1.' Tagged""change_pass""with"@v1.0.0*r1 > ""git"commit"*am"'Tag"the"database"@v1.0.0*r1.'""" [master"6661268]"Tag"the"database"@v1.0.0*r1. "1"file"changed,"1"insertion(+) > ""git"push

    Counting"objects:"5,"done. Writing"objects:"100%"(3/3),"346"bytes,"done. Total"3"(delta"2),"reused"0"(delta"0) To"../flipr*remote """0e31da9..6661268""master"*>"master > You’re It > 172 Wednesday, May 22, 13
  278. ""git"tag"v1.0.0*r1"*am"'Tag"v1.0.0*r1.' > ""sqitch"tag"v1.0.0*r1"*n"'Tag"v1.0.0*r1.' Tagged""change_pass""with"@v1.0.0*r1 > ""git"commit"*am"'Tag"the"database"@v1.0.0*r1.'""" [master"6661268]"Tag"the"database"@v1.0.0*r1. "1"file"changed,"1"insertion(+) > ""git"push

    Counting"objects:"5,"done. Writing"objects:"100%"(3/3),"346"bytes,"done. Total"3"(delta"2),"reused"0"(delta"0) To"../flipr*remote """0e31da9..6661268""master"*>"master > ""git"push"**tags To"../flipr*remote "*"[new"tag]"""""""""v1.0.0*r1"*>"v1.0.0*r1 > You’re It > 172 Wednesday, May 22, 13
  279. ""createdb"flipr_qa" > >"cd"flipr*1.0.0*r1 > Bundled? > ""sqitch"*d"flipr_qa"deploy Adding"metadata"tables"to"flipr_qa Deploying"changes"to"flipr_qa ""+"appschema"..............."ok

    ""+"users"..................."ok ""+"flips"..................."ok ""+"insert_user"@v1.0.0*r1".."ok > 174 Wednesday, May 22, 13
  280. ""createdb"flipr_qa" > >"cd"flipr*1.0.0*r1 > Bundled? > Ship it! ""sqitch"*d"flipr_qa"deploy Adding"metadata"tables"to"flipr_qa

    Deploying"changes"to"flipr_qa ""+"appschema"..............."ok ""+"users"..................."ok ""+"flips"..................."ok ""+"insert_user"@v1.0.0*r1".."ok > 174 Wednesday, May 22, 13
  281. antisocial network Merge Madness Merge everything Back to master users

    flips userfuncs Union merge sqitch.plan 175 Wednesday, May 22, 13
  282. antisocial network Merge Madness Merge everything Back to master users

    flips userfuncs Union merge sqitch.plan Bundle and ship 175 Wednesday, May 22, 13
  283. 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 175 Wednesday, May 22, 13
  284. You Know the Drill Write revert script Add test Use

    has_function() antisocial network 181 Wednesday, May 22, 13
  285. You Know the Drill Write revert script Add test Use

    has_function() Commit antisocial network 181 Wednesday, May 22, 13
  286. You Know the Drill Write revert script Add test Use

    has_function() Commit Push antisocial network 181 Wednesday, May 22, 13
  287. You Know the Drill Write revert script Add test Use

    has_function() Commit Push Modify the insert_user test antisocial network 181 Wednesday, May 22, 13
  288. ""git"diff"test/insert_user.sql @@"*5,7"+5,7"@@"SET"search_path"TO"flipr,public; " "**"Plan"the"tests. "BEGIN; *SELECT"plan(11); +SELECT"plan(12); " "SELECT"has_function("'insert_user'"); "SELECT"has_function(

    @@"*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'"); > 182 Wednesday, May 22, 13
  289. ""git"diff"test/insert_user.sql @@"*5,7"+5,7"@@"SET"search_path"TO"flipr,public; " "**"Plan"the"tests. "BEGIN; *SELECT"plan(11); +SELECT"plan(12); " "SELECT"has_function("'insert_user'"); "SELECT"has_function(

    @@"*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'"); > 182 Wednesday, May 22, 13
  290. ""git"diff"test/insert_user.sql @@"*5,7"+5,7"@@"SET"search_path"TO"flipr,public; " "**"Plan"the"tests. "BEGIN; *SELECT"plan(11); +SELECT"plan(12); " "SELECT"has_function("'insert_user'"); "SELECT"has_function(

    @@"*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'"); > 182 Wednesday, May 22, 13
  291. "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' *); +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')"$$, 183 Wednesday, May 22, 13
  292. "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' *); +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')"$$, 183 Wednesday, May 22, 13
  293. "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' *); +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')"$$, 183 Wednesday, May 22, 13
  294. "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' *); +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')"$$, 183 Wednesday, May 22, 13
  295. @@"*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; > 184 Wednesday, May 22, 13
  296. @@"*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; > 184 Wednesday, May 22, 13
  297. @@"*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; > 184 Wednesday, May 22, 13
  298. Consider this Change ""git"diff 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; > 188 Wednesday, May 22, 13
  299. Consider this Change ""git"diff 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? 188 Wednesday, May 22, 13
  300. Not So Much Copy insert_user.sql to new deploy file Change

    that new file antisocial network 189 Wednesday, May 22, 13
  301. 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 189 Wednesday, May 22, 13
  302. 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 189 Wednesday, May 22, 13
  303. 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 189 Wednesday, May 22, 13
  304. 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 189 Wednesday, May 22, 13
  305. >"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)); +$$; > 190 Wednesday, May 22, 13
  306. >"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. 190 Wednesday, May 22, 13
  307. ""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? > 193 Wednesday, May 22, 13
  308. ""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? > 193 Wednesday, May 22, 13
  309. ""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 193 Wednesday, May 22, 13
  310. ""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? > 193 Wednesday, May 22, 13
  311. ""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 193 Wednesday, May 22, 13
  312. What’s the Diff? ""diff"*u"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:"appschema

    " "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; > > 194 Wednesday, May 22, 13
  313. >"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! 199 Wednesday, May 22, 13
  314. ""git"add". >"git"commit"*m"'Update"insert_user"to"use"pgcrypto.' [master"59f5823]"Update"insert_user"to"use"pgcrypto. "8"files"changed,"66"insertions(+),"25"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.75"KiB,"done. Total"10"(delta"6),"reused"0"(delta"0) To"../flipr*remote """5f4c29a..59f5823""master"*>"master > > 200 Wednesday, May 22, 13
  315. antisocial network Change It Up git reset --hard insert_user2 Rework

    change_pass Use pgcrypo 201 Wednesday, May 22, 13
  316. antisocial network Change It Up git reset --hard insert_user2 Rework

    change_pass Use pgcrypo Test first! 201 Wednesday, May 22, 13
  317. antisocial network Change It Up git reset --hard insert_user2 Rework

    change_pass Use pgcrypo Test first! Bundle, tag, release 201 Wednesday, May 22, 13
  318. 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 201 Wednesday, May 22, 13
  319. 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. 203 Wednesday, May 22, 13
  320. I’m sorry I have no money left to pay you.

    antisocial network RIP 205 Wednesday, May 22, 13
  321. And that you’re able to use the skills you’ve gained

    in your next job. antisocial network RIP 207 Wednesday, May 22, 13
  322. MOAR Git Bisecting Blaming Pull requests Submitting patches Rewriting history

    Log formatting antisocial network RIP 209 Wednesday, May 22, 13
  323. MOAR Git Bisecting Blaming Pull requests Submitting patches Rewriting history

    Log formatting git help --all antisocial network RIP 209 Wednesday, May 22, 13
  324. pgTAP Skillz TDDD Schema testing Scalar testing Functional testing Relational

    testing antisocial network RIP 210 Wednesday, May 22, 13
  325. MOAR pgTAP Testing privileges Mocking interfaces Custom test functions xUnit-style

    testing antisocial network RIP 211 Wednesday, May 22, 13
  326. MOAR pgTAP Testing privileges Mocking interfaces Custom test functions xUnit-style

    testing Tests maintained in functions antisocial network RIP 211 Wednesday, May 22, 13
  327. MOAR pgTAP Testing privileges Mocking interfaces Custom test functions xUnit-style

    testing Tests maintained in functions http:/ /pgtap.org/documentation.html antisocial network RIP 211 Wednesday, May 22, 13
  328. Sqitch Skillz Adding changes Dependency management Deploying changes Verifying changes

    Reverting changes antisocial network RIP 212 Wednesday, May 22, 13
  329. Sqitch Skillz Adding changes Dependency management Deploying changes Verifying changes

    Reverting changes Rebasing changes antisocial network RIP 212 Wednesday, May 22, 13
  330. Sqitch Skillz Adding changes Dependency management Deploying changes Verifying changes

    Reverting changes Rebasing changes Reworking changes antisocial network RIP 212 Wednesday, May 22, 13
  331. Sqitch Skillz Adding changes Dependency management Deploying changes Verifying changes

    Reverting changes Rebasing changes Reworking changes antisocial network RIP 212 Wednesday, May 22, 13
  332. MOAR Sqitch Cross-project dependencies Multiple projects, one database Changing Branches

    Checkout command antisocial network RIP 213 Wednesday, May 22, 13
  333. MOAR Sqitch Cross-project dependencies Multiple projects, one database Changing Branches

    Checkout command Reverts to last common change antisocial network RIP 213 Wednesday, May 22, 13
  334. MOAR Sqitch Cross-project dependencies Multiple projects, one database Changing Branches

    Checkout command Reverts to last common change Changes Git branch antisocial network RIP 213 Wednesday, May 22, 13
  335. MOAR Sqitch Cross-project dependencies Multiple projects, one database Changing Branches

    Checkout command Reverts to last common change Changes Git branch Deploys antisocial network RIP 213 Wednesday, May 22, 13
  336. MOAR Sqitch Cross-project dependencies Multiple projects, one database Changing Branches

    Checkout command Reverts to last common change Changes Git branch Deploys sqitch help antisocial network RIP 213 Wednesday, May 22, 13
  337. Agile Database Development David E. Wheeler PGCon 2013 Ottawa 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 215 Wednesday, May 22, 13