view src/nabble/model/DbSiteUpdater.jmp @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children
line wrap: on
line source

<%
package nabble.model;

import fschmidt.db.DbDatabase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;


final class DbSiteUpdater {
	private static final Logger logger = LoggerFactory.getLogger(DbSiteUpdater.class);

	// don't forget to update site.schema
	static final int version = 167;

	private static class Result {
		private static final long wait = 2000L;  // 2 seconds
		private final long waitUntil = System.currentTimeMillis() + wait;
		private DbSiteUpdater updater;
		private SQLException sqlException = null;
		private RuntimeException runtimeException = null;
		private Error error = null;
		private final String user;

		private Result(DbDatabase db,String user) {
			updater = new DbSiteUpdater(db,this,user);
			this.user = user;
			updater.start();
		}

		private synchronized void check() throws SQLException {
			if( !isDone() ) {
				long now = System.currentTimeMillis();
				if( now < waitUntil ) {
					try {
						wait( waitUntil - now );
					} catch(InterruptedException e) {
						throw new RuntimeException(e);
					}
				}
				if( !isDone() )
					throw new UpdatingException(user);
			}
			if( sqlException != null )
				throw sqlException;
			if( runtimeException != null )
				throw runtimeException;
			if( error != null )
				throw error;
		}

		synchronized boolean isDone() {
			return updater==null;
		}

		synchronized void done(Throwable e) {
			updater = null;
			if( e != null ) {
				if( e instanceof SQLException ) {
					sqlException = (SQLException)e;
				} else if( e instanceof RuntimeException ) {
					runtimeException = (RuntimeException)e;
				} else if( e instanceof Error ) {
					error = (Error)e;
				} else {
					runtimeException = new RuntimeException("invalid exception type",e);
				}
			}
			notifyAll();
		}
	}

	private static final Map<String,Result> userResults = new HashMap<String,Result>();
	private static final Object lock = new Object();

	static void update(String user,DbDatabase db) throws SQLException {
		if( user.equals("global") )
			return;  // done from command line
		getResult(user,db).check();
	}

	private static Result getResult(String user,DbDatabase db) throws SQLException {
		synchronized(lock) {
			Result result = userResults.get(user);
			if( result == null ) {
				result = new Result(db,user);
				userResults.put(user,result);
			}
			return result;
		}
	}

	// for shell
	public static void clearResult(long siteId) {
		String user = SiteKey.getInstance(siteId).schema();
		synchronized(lock) {
			userResults.remove(user);
		}
	}




	private final Result result;
	private final DbUpdater up;

	private DbSiteUpdater(DbDatabase db,Result result,String schema) {
		this.up = new DbUpdater(db,schema);
		this.result = result;
	}

	private void start() {
		try {  // needed for site creation because version is only up to date in current transaction
			if( up.dbVersion() == version ) {
				result.done(null);
				return;
			}
		} catch(RuntimeException e) {
			result.done(e);
			return;
		} catch(Error e) {
			result.done(e);
			return;
		}
		Executors.executeNow(new Runnable(){public void run(){
			try {
				go();
				result.done(null);
			} catch(SQLException e) {
				result.done(e);
			} catch(RuntimeException e) {
				result.done(e);
			} catch(Error e) {
				result.done(e);
			} finally {
				if( !result.isDone() )
					logger.error("result isn't done");
			}
		}});
	}

	void go() throws SQLException {
		try {
			update();
			if( up.databaseVersion() != version )
				throw new RuntimeException();
		} finally {
			up.close();
		}
	}

	@Override public String toString() {
		return "DbSiteUpdater-" + up.schema;
	}

	private void execCatching(String sql,String exStr) throws SQLException {
		up.begin();
		try {
			up.exec(sql);
		} catch(SQLException e) {
			if( !e.getMessage().contains(exStr) )
				throw e;
			logger.info(e.toString());
			up.exec("rollback");
		}
		up.commit();
	}

	void update() throws SQLException {
		switch( up.dbVersion() ) {
		case 0:
			up.execStep( %>
				create index embed_key_site_idx on embed_key(site_id)
			<% );
		case 1:
			up.execStep( %>
				create index file_avatar_site_idx on file_avatar(site_id)
			<% );
		case 2:
			up.execStep( %>
				create index file_node_site_idx on file_node(site_id)
			<% );
		case 3:
			up.execStep( %>
				create index file_temp_site_idx on file_temp(site_id)
			<% );
		case 4:
			up.execStep( %>
				create index mailing_list_site_idx on mailing_list(site_id)
			<% );
		case 5:
			up.execStep( %>
				create index node_msg_site_idx on node_msg(site_id)
			<% );
		case 6:
			up.execStep( %>
				create index node_property_site_idx on node_property(site_id)
			<% );
		case 7:
			up.execStep( %>
				create index paid_site_idx on paid(site_id)
			<% );
		case 8:
			up.execStep( %>
				create index registration_site_idx on registration(site_id)
			<% );
		case 9:
			up.execStep( %>
				create index subscription_site_idx on subscription(site_id)
			<% );
		case 10:
			up.execStep( %>
				create index user_property_site_idx on user_property(site_id)
			<% );
		case 11:
			up.execStep( %>
				create index view_count_site_idx on view_count(site_id)
			<% );
		case 12:
			up.execStep( %>
				create index visited_site_idx on visited(site_id)
			<% );

		case 13:
			up.execStep( %>
				create index tag_node_idx on tag(node_id)
			<% );
		case 14:
			up.execStep( %>
				create index tag_user_idx on tag(user_id)
			<% );
		case 15:
			up.execStep( %>
				create index embed_key_node_idx on embed_key(node_id)
			<% );
		case 16:
			up.execStep( %>
				create index visited_node_idx on visited(node_id)
			<% );
		case 17:
			up.execStep( %>
				create index visited_last_node_idx on visited(last_node_id)
			<% );
		case 18:
			up.execStep( %>
				alter table node add column poll_option_count integer default null
			<% );
		case 19:
			up.execStep( %>
				alter table configuration drop column site_id
			<% );
		case 20:
			up.execStep( %>
				alter table configuration ADD PRIMARY KEY (name)
			<% );
		case 21:
			up.execStep( %>
				alter table embed_key drop column site_id
			<% );
		case 22:
			up.execStep( %>
				alter table file_avatar drop column site_id
			<% );
		case 23:
			up.execStep( %>
				alter table file_node drop column site_id
			<% );
		case 24:
			up.execStep( %>
				alter table file_temp drop column site_id
			<% );
		case 25:
			up.execStep( %>
				alter table mailing_list drop column site_id
			<% );
		case 26:
			up.execStep( %>
				alter table node_msg drop column site_id
			<% );
		case 27:
			up.execStep( %>
				alter table node_property drop column site_id
			<% );
		case 28:
			up.execStep( %>
				alter table paid drop column site_id
			<% );
		case 29:
			up.execStep( %>
				alter table registration drop column site_id
			<% );
		case 30:
			up.execStep( %>
				alter table subscription drop column site_id
			<% );
		case 31:
			up.execStep( %>
				alter table user_property drop column site_id
			<% );
		case 32:
			up.execStep( %>
				alter table view_count drop column site_id
			<% );
		case 33:
			up.execStep( %>
				alter table visited drop column site_id
			<% );
		case 34:
			up.execStep( %>
				alter table file_site drop column site_id
			<% );
		case 35:
			up.execStep( %>
				alter table file_site ADD PRIMARY KEY (name)
			<% );
		case 36:
			up.execStep( %>
				alter table module drop column site_id
			<% );
		case 37:
			up.execStep( %>
				alter table module ADD PRIMARY KEY (module_name)
			<% );
		case 38:
			up.execStep( %>
				alter table site_property drop column site_id
			<% );
		case 39:
			up.execStep( %>
				alter table site_property ADD PRIMARY KEY (key)
			<% );
		case 40:
			up.execStep( %>
				alter table tweak drop column site_id
			<% );
		case 41:
			up.execStep( %>
				alter table tweak ADD PRIMARY KEY (tweak_name)
			<% );
		case 42:
			up.execStep( %>
				alter table tag drop column site_id
			<% );
		case 43:
			up.execStep( %>
				CREATE UNIQUE INDEX tag_idx
					ON tag (node_id, user_id, label);
			<% );
		case 44:
			up.execStep( %>
				drop index tag_node_idx
			<% );
		case 45:
			up.execStep( %>
				alter table user_ drop column site_id
			<% );
		case 46:
			up.execStep( %>
				CREATE UNIQUE INDEX user_email_idx
					ON user_ (lower(email));
			<% );
		case 47:
			up.execStep( %>
				CREATE UNIQUE INDEX user_name_idx
					ON user_ (lower(name));
			<% );
		case 48:
			up.execStep( %>
				alter table node drop column site_id
			<% );
		case 49:
			up.execStep( %>
				CREATE INDEX node_cookie_idx
					ON node (cookie)
					WHERE cookie IS NOT NULL;
			<% );
		case 50:
			up.execStep( %>
				CREATE INDEX node_message_id_idx
					ON node (lower(message_id))
					WHERE message_id IS NOT NULL;
			<% );
		case 51:
			up.execStep( %>
				drop table embed_key
			<% );
		case 52:
			up.execStep( %>
				delete from site_property where key = '__embedding_warning'
			<% );
		case 53:
			up.execStep( %>
				alter table site drop column site_id
			<% );
		case 54:
			up.execStep( %>
				alter table site add column one integer default 1 not null
			<% );
		case 55:
			up.execStep( %>
				alter table site ADD PRIMARY KEY (one)
			<% );
		case 56:
			up.execStep( %>
				alter table site add column content_type character varying;
			<% );
		case 57:
			up.execStep( %>
				update configuration set naml=E'<override_macro name="site_style">\n\t<n.overridden/>\n\t#search-box { text-align:left; }\n</override_macro>' where name='searchBoxAlignment';
			<% );
		case 58:
			up.execStep( %>
				update site set ad_type=null where ad_type='ADBRITE' or ad_type='ADSENSE'
			<% );

		case 59:
			up.execStep( %>
				alter table node
					add column unsafe character varying default 'new',
					add column safe_version integer
			<% );
		case 60:
			up.execStep( %>
				alter table user_
					add column unsafe character varying default 'new',
					add column safe_version integer
			<% );

		case 61:
			up.execStep( %>
				update site set ad_type='HOPELESS' where ad_type='COPYRIGHTED'
			<% );
		case 62:
			up.execStep( %>
				update site set ad_type='HOPELESS' where ad_type='OURS'
			<% );
		case 63:
			up.execStep( %>
				update site set ad_type=null, ad_free=<%=Integer.MAX_VALUE%> where ad_type='NONE'
			<% );

		case 64:
			up.execStep( %>
				alter table node
					add column is_safe boolean
			<% );
		case 65:
			up.execStep( %>
				alter table user_
					add column is_safe boolean
			<% );
		case 66:
			up.execStep( null );
		case 67:
			up.execStep( null );
		case 68:
			up.execStep( %>
				delete from tag where label='hide_ads'
			<% );
		case 69:
			up.execStep( %>
				update site set ad_type=null where ad_type='SOME_ADSENSE'
			<% );
		case 70:
			up.execStep( %>
				update site set ad_type='ADULT' where ad_type='HACK'
			<% );
		case 71:
			up.execStep( %>
				update site set content_type=null where content_type='FAMILY'
			<% );
		case 72:
			up.execStep( %>
				drop index if exists node_is_safe_idx
			<% );
		case 73:
			up.execStep( %>
				drop index if exists user_is_safe_idx
			<% );
		case 74:
			up.execStep( %>
				create index node_unsafe_idx on node(unsafe) where unsafe is not null and unsafe!='new'
			<% );
		case 75:
			up.execStep( %>
				create index user_unsafe_idx on user_(unsafe) where unsafe is not null and unsafe!='new'
			<% );
		case 76:
			up.execStep( %>
				update site set content_type=ad_type where content_type is null and ad_type is not null and ad_type!='HOPELESS'
			<% );
		case 77:
			up.execStep( %>
				update site set ad_type=content_type where content_type is not null and ad_type is null
			<% );
		case 78:
			up.execStep( %>
				update site set ad_type='ADULT' where content_type='ADULT' and ad_type='PORN'
			<% );

		case 79:
			up.execStep( %>
				alter table user_
					drop column unsafe,
					drop column safe_version,
					drop column is_safe
			<% );
		case 80:
			up.execStep( null );
		case 81:
			up.execStep( null );
		case 82:
			up.begin();
			up.exec( %>
				alter table node
					drop column unsafe,
					drop column safe_version,
					drop column is_safe
			<% );
			up.exec( %>
				alter table node
					add column unsafe character varying,
					add column safe_version integer,
					add column is_safe boolean
			<% );
			up.commit();
		case 83:
			up.execStep( %>
				create index node_unsafe_idx on node(unsafe) where safe_version is not null
			<% );
		case 84:
			up.execStep( %>
				delete from module where module_name = 'topic_ads';
			<% );
		case 85:
			up.execStep( %>
				alter table node
					drop column unsafe,
					drop column safe_version,
					drop column is_safe
			<% );
		case 86:
			up.execStep( %>
				alter table site
					add column when_created timestamp with time zone NOT NULL DEFAULT now()
			<% );
		case 87:
			up.execStep( %>
				update site
					set when_created = node.when_created
					from node
					where node.node_id = site.root_node_id
			<% );

		case 88:
			up.execStep( %>
				alter table site
					add column is_safe boolean NOT NULL DEFAULT true
			<% );
		case 89:
			up.execStep( %>
				update site set is_safe = false where ad_type is not null
			<% );
		case 90:
			up.execStep( %>
				update site set when_created = now() where not is_safe
			<% );
		case 91:
			up.execStep( %>
				alter table site rename column ad_type to old_ad_type
			<% );
		case 92:
			up.execStep( %>
				alter table site rename column content_type to old_content_type
			<% );
		case 93:
			up.execStep( %>
				alter table site
					add column ad_credits_for_users boolean NOT NULL DEFAULT false
			<% );
		case 94:
			up.execStep( %>
				alter table site
					add column monthly_views integer NOT NULL DEFAULT 0
			<% );
		case 95:
			up.execStep( %>
				delete from tag where label='group:Administrators' and node_id is null and user_id = (select user_id from user_ where email = 'pedro@nabble.com')
			<% );

		case 96:
			execCatching( %>
				alter table tweak ADD PRIMARY KEY (tweak_name)
			<%, "multiple primary keys for table" );
		case 97:
			execCatching( %>
				alter table mailing_list ADD PRIMARY KEY (node_id)
			<%, "multiple primary keys for table" );
		case 98:
/*
			execCatching( %>
				alter table mailing_list ADD CONSTRAINT mailing_list_node_id_fkey FOREIGN KEY (node_id) REFERENCES node (node_id) ON DELETE CASCADE
			<%, "already exists" );
*/
			up.execStep( null );
		case 99:
			execCatching( %>
				CREATE INDEX forum_ml_idx ON mailing_list (lower(list_address))
			<%, "already exists" );
		case 100:
			execCatching( %>
				alter table node ADD PRIMARY KEY (node_id)
			<%, "multiple primary keys for table" );
		case 101:
			execCatching( %>
				alter table configuration ADD PRIMARY KEY (name)
			<%, "multiple primary keys for table" );
		case 102:
			execCatching( %>
				alter table file_avatar ADD PRIMARY KEY (user_id, name)
			<%, "multiple primary keys for table" );
		case 103:
			execCatching( %>
				alter table file_node ADD PRIMARY KEY (node_id, name)
			<%, "multiple primary keys for table" );
		case 104:
			execCatching( %>
				alter table file_site ADD PRIMARY KEY (name)
			<%, "multiple primary keys for table" );
		case 105:
			execCatching( %>
				alter table file_temp ADD PRIMARY KEY (user_id, name)
			<%, "multiple primary keys for table" );
		case 106:
			execCatching( %>
				CREATE INDEX file_temp_date_idx ON file_temp (date_)
			<%, "already exists" );
		case 107:
			execCatching( %>
				alter table module ADD PRIMARY KEY (module_name)
			<%, "multiple primary keys for table" );
		case 108:
			execCatching( %>
				CREATE INDEX node_cookie_idx ON node (cookie) WHERE cookie IS NOT NULL
			<%, "already exists" );
		case 109:
			execCatching( %>
				CREATE INDEX node_export ON node (export_permalink) WHERE export_permalink IS NOT NULL
			<%, "already exists" );
		case 110:
			execCatching( %>
				CREATE INDEX node_message_id_idx ON node (lower(message_id)) WHERE message_id IS NOT NULL
			<%, "already exists" );
		case 111:
			execCatching( %>
				CREATE INDEX node_ml2_idx ON node (lower(parent_message_id)) WHERE lower(parent_message_id) IS NOT NULL
			<%, "already exists" );
		case 112:
			execCatching( %>
				CREATE INDEX node_owner_idx ON node (owner_id, when_created)
			<%, "already exists" );
		case 113:
			execCatching( %>
				CREATE UNIQUE INDEX node_parent2_idx ON node (parent_id, when_created, node_id)
			<%, "already exists" );
		case 114:
			execCatching( %>
				CREATE UNIQUE INDEX node_parent_idx ON node (parent_id, last_node_date, node_id)
			<%, "already exists" );
		case 115:
			execCatching( %>
				CREATE INDEX node_sent_idx ON node (when_sent) WHERE when_sent IS NOT NULL
			<%, "already exists" );
		case 116:
			execCatching( %>
				CREATE UNIQUE INDEX pinned_node_idx ON node (parent_id, pin) WHERE pin IS NOT NULL
			<%, "already exists" );
		case 117:
			execCatching( %>
				CREATE INDEX post_date ON node (when_created)
			<%, "already exists" );
		case 118:
			execCatching( %>
				alter table node_msg ADD PRIMARY KEY (node_id)
			<%, "multiple primary keys for table" );
		case 119:
			execCatching( %>
				alter table node_property ADD PRIMARY KEY (node_id, key)
			<%, "multiple primary keys for table" );
		case 120:
			execCatching( %>
				alter table paid ADD PRIMARY KEY (user_id)
			<%, "multiple primary keys for table" );
		case 121:
			execCatching( %>
				alter table registration ADD PRIMARY KEY (key_)
			<%, "multiple primary keys for table" );
		case 122:
			execCatching( %>
				CREATE INDEX registration_date_idx ON registration (date_)
			<%, "already exists" );
		case 123:
			execCatching( %>
				alter table site ADD PRIMARY KEY (one)
			<%, "multiple primary keys for table" );
		case 124:
			execCatching( %>
				alter table site_property ADD PRIMARY KEY (key)
			<%, "multiple primary keys for table" );
		case 125:
			execCatching( %>
				alter table subscription ADD PRIMARY KEY (user_id, node_id)
			<%, "multiple primary keys for table" );
		case 126:
			execCatching( %>
				CREATE INDEX subscription_node_idx ON subscription (node_id)
			<%, "already exists" );
		case 127:
			execCatching( %>
				CREATE UNIQUE INDEX tag_idx ON tag (node_id, user_id, label)
			<%, "already exists" );
		case 128:
			execCatching( %>
				CREATE INDEX tag_user_idx ON tag (user_id)
			<%, "already exists" );
		case 129:
			execCatching( %>
				alter table user_ ADD PRIMARY KEY (user_id)
			<%, "multiple primary keys for table" );
		case 130:
			execCatching( %>
				CREATE UNIQUE INDEX user_email_idx ON user_ (lower(email))
			<%, "already exists" );
		case 131:
			execCatching( %>
				CREATE UNIQUE INDEX user_name_idx ON user_ (lower(name))
			<%, "already exists" );
		case 132:
			execCatching( %>
				alter table user_property ADD PRIMARY KEY (user_id, key)
			<%, "multiple primary keys for table" );
		case 133:
			execCatching( %>
				alter table view_count ADD PRIMARY KEY (node_id)
			<%, "multiple primary keys for table" );
		case 134:
			execCatching( %>
				alter table visited ADD PRIMARY KEY (user_id, node_id)
			<%, "multiple primary keys for table" );
		case 135:
			execCatching( %>
				CREATE INDEX visited_last_node_idx ON visited (last_node_id)
			<%, "already exists" );
		case 136:
			execCatching( %>
				CREATE INDEX visited_node_idx ON visited (node_id)
			<%, "already exists" );

		case 137:
			execCatching( %>
				ALTER TABLE ONLY file_avatar 
					ADD CONSTRAINT file_avatar_user_id_fkey FOREIGN KEY (user_id) REFERENCES user_(user_id) ON DELETE CASCADE
			<%, "already exists" );
		case 138:
			up.execStep( null );
		case 139:
			execCatching( %>
				ALTER TABLE ONLY file_temp 
					ADD CONSTRAINT file_temp_user_id_fkey FOREIGN KEY (user_id) REFERENCES user_(user_id) ON DELETE CASCADE
			<%, "already exists" );
		case 140:
			execCatching( %>
				ALTER TABLE ONLY mailing_list 
					ADD CONSTRAINT mailing_list_node_id_fkey FOREIGN KEY (node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 141:
			up.execStep( null );
		case 142:
			execCatching( %>
				ALTER TABLE ONLY node 
					ADD CONSTRAINT node_new_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES user_(user_id)
			<%, "already exists" );
		case 143:
			execCatching( %>
				ALTER TABLE ONLY node 
					ADD CONSTRAINT node_node_id_fkey FOREIGN KEY (node_id) REFERENCES node_msg(node_id) DEFERRABLE INITIALLY DEFERRED
			<%, "already exists" );
		case 144:
			up.execStep( null );
		case 145:
			up.execStep( null );
		case 146:
			execCatching( %>
				ALTER TABLE ONLY paid 
					ADD CONSTRAINT paid_user_id_fkey FOREIGN KEY (user_id) REFERENCES user_(user_id) ON DELETE CASCADE
			<%, "already exists" );
		case 147:
			execCatching( %>
				ALTER TABLE ONLY site 
					ADD CONSTRAINT site_root_node_id_fkey FOREIGN KEY (root_node_id) REFERENCES node(node_id) DEFERRABLE INITIALLY DEFERRED
			<%, "already exists" );
		case 148:
			up.execStep( null );
		case 149:
			execCatching( %>
				ALTER TABLE ONLY subscription 
					ADD CONSTRAINT subscription_user_id_fkey FOREIGN KEY (user_id) REFERENCES user_(user_id) ON DELETE CASCADE
			<%, "already exists" );
		case 150:
			up.execStep( null );
		case 151:
			execCatching( %>
				ALTER TABLE ONLY tag 
					ADD CONSTRAINT tag_user_id_fkey FOREIGN KEY (user_id) REFERENCES user_(user_id) ON DELETE CASCADE
			<%, "already exists" );
		case 152:
			execCatching( %>
				ALTER TABLE ONLY user_property 
					ADD CONSTRAINT user_property_user_id_fkey FOREIGN KEY (user_id) REFERENCES user_(user_id) ON DELETE CASCADE
			<%, "already exists" );
		case 153:
			up.execStep( null );
		case 154:
			up.execStep( null );
		case 155:
			up.execStep( null );
		case 156:
			execCatching( %>
				ALTER TABLE ONLY visited 
					ADD CONSTRAINT visited_user_id_fkey FOREIGN KEY (user_id) REFERENCES user_(user_id) ON DELETE CASCADE
			<%, "already exists" );
		case 157:
			up.execStep( null );

		// problematic constraints
		case 158:
			execCatching( %>
				ALTER TABLE ONLY file_node 
					ADD CONSTRAINT file_node_node_id_fkey FOREIGN KEY (node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 159:
			execCatching( %>
				ALTER TABLE ONLY node_msg 
					ADD CONSTRAINT node_msg_node_id_fkey FOREIGN KEY (node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 160:
			execCatching( %>
				ALTER TABLE ONLY node 
					ADD CONSTRAINT node_parent_id_fkey FOREIGN KEY (parent_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 161:
			execCatching( %>
				ALTER TABLE ONLY node_property 
					ADD CONSTRAINT node_property_node_id_fkey FOREIGN KEY (node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 162:
			execCatching( %>
				ALTER TABLE ONLY subscription 
					ADD CONSTRAINT subscription_node_id_fkey FOREIGN KEY (node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 163:
			execCatching( %>
				ALTER TABLE ONLY tag 
					ADD CONSTRAINT tag_node_id_fkey FOREIGN KEY (node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 164:
			execCatching( %>
				ALTER TABLE ONLY view_count 
					ADD CONSTRAINT view_count_node_id_fkey FOREIGN KEY (node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 165:
			execCatching( %>
				ALTER TABLE ONLY visited 
					ADD CONSTRAINT visited_last_node_id_fkey FOREIGN KEY (last_node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		case 166:
			execCatching( %>
				ALTER TABLE ONLY visited 
					ADD CONSTRAINT visited_node_id_fkey FOREIGN KEY (node_id) REFERENCES node(node_id) ON DELETE CASCADE
			<%, "already exists" );
		}
	}
}
%>