2f30a118359e2f3a989a6ca72111fc54952eea67
[quassel.git] / src / core / SQL / upgradeSchema.sh
1 #!/bin/bash
2 # Copyright (C) 2005-2016 by the Quassel Project - devel@quassel-irc.org
3 # Licensed under GNU General Public License version 2, or (at your option)
4 # version 3.
5 #
6 # "One does not simply 'upgrade the schema'..."
7 #
8 # When changing Quassel's database schema, you need to follow several steps to
9 # handle all cases (upgrade, Postgres migration, etc).
10 #
11 # 1.  Run this script on -both- the PostgreSQL and SQLite directory
12 #
13 # Make sure you're on the Git branch you want to modify
14 # > ./upgradeSchema.sh "PostgreSQL"
15 # > ./upgradeSchema.sh "SQLite"
16 #
17 # 2.  Modify queries and setup scripts to handle your change
18 #
19 # [Example] Modifying the 'ircserver' table to add column 'test'
20 # Modify all query/setup .sql files that touch the 'ircserver' table for
21 # -both- PostgreSQL and SQLite.
22 #
23 # 3.  Create an upgrade script for -both- PostgreSQL and SQLite
24 #
25 # [Example] Modifying the 'ircserver' table to add column 'test'
26 # Add the file 'upgrade_000_alter_ircserver_add_test.sql' with contents:
27 # > ALTER TABLE ircserver
28 # > ADD COLUMN test [additional column-specific details]
29 #
30 # 4.  Create a pair of migration scripts for moving from SQLite to PostgreSQL
31 #
32 # [Example] Modifying the 'ircserver' table to add column 'test'
33 # > Modify 'SQLite/##/migrate_read_ircserver.sql' to select from new column
34 # > Modify 'PostgreSQL/##/migrate_write_ircserver.sql' to insert to new column
35 #
36 # 5.  Update the SQL resource file; re-run CMake if needed
37 #
38 # The easy way: run "updateSQLResource.sh" in this directory.
39 #
40 # The manual way:
41 # Add the new SQL queries to 'src/core/sql.qrc', update all existing files.
42 #
43 # [Example] Modifying the 'ircserver' table to add column 'test'
44 # > Add the new upgrade script...
45 #   <file>./SQL/SQLite/19/upgrade_000_alter_ircserver_add_test.sql</file>
46 #   <file>./SQL/PostgreSQL/18/upgrade_000_alter_ircserver_add_test.sql</file>
47 # > Find/replace all non-upgrade scripts from the old schema number to new one
48 #   <file>./SQL/SQLite/[18->19]/update_buffer_persistent_channel.sql</file>
49 #   <file>./SQL/PostgreSQL/[17->18]/update_buffer_persistent_channel.sql</file>
50 #   (etc)
51 #
52 # 6.  Update the migration logic in 'src/core/abstractsqlstorage.h', and the
53 # storage backends 'postgresqlstorage.cpp' and 'sqlitestorage.cpp'
54 #
55 # [Example] Modifying the 'ircserver' table to add column 'test'
56 # > Modify struct 'IrcServerMO' in 'abstractsqlstorage.h', adding an entry for
57 #   'test' of the appropriate data-type.
58 # > Modify 'SqliteMigrationReader::readMo(IrcServerMO &ircserver)' in
59 #   'sqlitestorage.cpp' to read from the new column and store it in the
60 #   migration object.  You may need to convert from SQLite's looser types.
61 # > Modify 'PostgreSqlMigrationWriter::writeMo(const IrcServerMO &ircserver)'
62 #   in 'postgresqlstorage.cpp' to write to the new column from the data in the
63 #   migration object.
64 #
65 # 7.  Update any affected queries in storage backends 'postgresqlstorage.cpp'
66 # and 'sqlitestorage.cpp', and any related synchronized 'src/common' classes.
67 #
68 # [Example] Modifying the 'ircserver' table to add column 'test'
69 # > Update 'network.h' to add new column to Server structure
70 #   QString proxyPass;                                     // Existing code
71 #   Typename test;                                         // New column 'test'
72 #   [...]
73 #   Server() : port(6667), ..., proxyPort(8080), test("defaultValue") {}
74 # > Modify reading data in ____Storage::networks(...)
75 #   server.proxyPass = serversQuery.value(10).toString();  // Existing code
76 #   server.test = serversQuery.value(11).toType();         // New column 'test'
77 #   servers << server;                                     // Existing code
78 # > Modify writing data in ____Storage::bindServerInfo(...)
79 #   query.bindValue(":proxypass", server.proxyPass);       // Existing code
80 #   query.bindValue(":test", server.test);                 // New column 'test'
81 #
82 # 8.  If protocol changed (add a setting, etc), add a new "Feature" flag
83 #
84 # Newer clients need to detect when they're on an older core to disable the
85 # feature.  Use 'enum Feature' in 'quassel.h'.  In client-side code, test with
86 # 'if (Client::coreFeatures() & Quassel::FeatureName) { ... }'
87 #
88 # 9.  Test everything!  Upgrade, migrate, new setups, new client/old core,
89 # old client/new core, etc.
90
91 TARGET_DIR="$1"
92 # If not specified, assume current directory
93 if [ ! "$TARGET_DIR" ]; then
94     TARGET_DIR="$(pwd)"
95 fi
96
97 if [[ ! -d "$TARGET_DIR" ]]; then
98     echo "No such directory '$TARGET_DIR'"
99     exit 1
100 fi
101
102 cd "$TARGET_DIR"
103
104 # Grab the current schema version
105 CURRENT_VERSION=$(ls | sort -n | tail -n1)
106
107 if [ ! $CURRENT_VERSION ]; then
108     echo "No previous schema found to upgrade from"
109     exit 2
110 fi
111
112 # Increment by one
113 ((NEW_VERSION=$CURRENT_VERSION + 1))
114
115 # Create the new schema directory, add the directory, and move all files over...
116 mkdir "$NEW_VERSION"
117 git add "$NEW_VERSION"
118 # ...except for 'upgrade_' scripts.
119 find "$CURRENT_VERSION" -maxdepth 1 -type f \! -name "upgrade_*" \! -name ".*" -exec git mv {} "$NEW_VERSION" \;