Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Implement dynamic schema modification -- both up- and downgrade -- to allow repo downloaded from remote to match properly with what the locally installed pkgng expects.
Matthew Seaman committed 13 years ago
commit b272952bc8c75585d3aa5e8dd0434ac3f8185a6a
parent ea742d9
8 files changed +501 -102
modified libpkg/pkg.h.in
@@ -1139,6 +1139,7 @@ typedef enum {
	PKG_EVENT_INTEGRITYCHECK_BEGIN,
	PKG_EVENT_INTEGRITYCHECK_FINISHED,
	PKG_EVENT_NEWPKGVERSION,
+
	PKG_EVENT_NOTICE,
	/* errors */
	PKG_EVENT_ERROR,
	PKG_EVENT_ERRNO,
@@ -1171,6 +1172,9 @@ struct pkg_event {
			char *msg;
		} e_pkg_error;
		struct {
+
			char *msg;
+
		} e_pkg_notice;
+
		struct {
			const char *url;
			off_t total;
			off_t done;
modified libpkg/pkg_event.c
@@ -83,6 +83,11 @@ pipeevent(struct pkg_event *ev)
		    "\"data\": {\"msg\": \"%s\"}}",
		    sbuf_json_escape(buf, ev->e_pkg_error.msg));
		break;
+
	case PKG_EVENT_NOTICE:
+
		sbuf_printf(msg, "{ \"type\": \"NOTICE\", "
+
		    "\"data\": {\"msg\": \"%s\"}}",
+
		    sbuf_json_escape(buf, ev->e_pkg_notice.msg));
+
		break;
	case PKG_EVENT_DEVELOPER_MODE:
		sbuf_printf(msg, "{ \"type\": \"ERROR\", "
		    "\"data\": {\"msg\": \"DEVELOPER_MODE: %s\"}}",
@@ -351,6 +356,22 @@ pkg_emit_error(const char *fmt, ...)
}

void
+
pkg_emit_notice(const char *fmt, ...)
+
{
+
	struct pkg_event ev;
+
	va_list ap;
+

+
	ev.type = PKG_EVENT_NOTICE;
+

+
	va_start(ap, fmt);
+
	vasprintf(&ev.e_pkg_notice.msg, fmt, ap);
+
	va_end(ap);
+

+
	pkg_emit_event(&ev);
+
	free(ev.e_pkg_error.msg);
+
}
+

+
void
pkg_emit_developer_mode(const char *fmt, ...)
{
	struct pkg_event ev;
modified libpkg/pkg_repo.c
@@ -2,7 +2,7 @@
 * Copyright (c) 2011-2013 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
-
 * Copyright (c) 2012 Matthew Seaman <matthew@FreeBSD.org>
+
 * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -45,15 +45,16 @@
#include "private/utils.h"
#include "private/pkg.h"
#include "private/pkgdb.h"
+
#include "private/repodb.h"
#include "private/thd_repo.h"

/* The package repo schema major revision */
-
#define REPO_SCHEMA_MAJOR 3
+
#define REPO_SCHEMA_MAJOR 2

/* The package repo schema minor revision.
   Minor schema changes don't prevent older pkgng
-
   versions accessing the repo */
-
#define REPO_SCHEMA_MINOR 0
+
   versions accessing the repo. */
+
#define REPO_SCHEMA_MINOR 3

#define REPO_SCHEMA_VERSION (REPO_SCHEMA_MAJOR * 1000 + REPO_SCHEMA_MINOR)

@@ -68,6 +69,8 @@ typedef enum _sql_prstmt_index {
	SHLIB1,
	SHLIB_REQD,
	SHLIB_PROV,
+
	ABSTRACT1,
+
	ABSTRACT2,
	EXISTS,
	VERSION,
	DELETE,
@@ -141,6 +144,20 @@ static sql_prstmt sql_prepared_statements[PRSTMT_LAST] = {
		"SELECT count(*) FROM packages WHERE cksum=?1",
		"T",
	},
+
	[ABSTRACT1] = {
+
		NULL,
+
		"INSERT OR IGNORE INTO abstract(abstract) "
+
		"VALUES (?1)",
+
		"T",
+
	},
+
	[ABSTRACT2] = {
+
		NULL,
+
		"INSERT OR ROLLBACK INTO pkg_abstract(package_id, key_id, value_id) "
+
		"VALUES (?1,"
+
		" (SELECT abstract_id FROM abstract WHERE abstract=?2),"
+
		" (SELECT abstract_id FROM abstract WHERE abstract=?3))",
+
		"ITT",
+
	},
	[VERSION] = {
		NULL,
		"SELECT version FROM packages WHERE origin=?1",
@@ -308,6 +325,26 @@ get_repo_user_version(sqlite3 *sqlite, const char *database, int *reposcver)
}

static int
+
set_repo_user_version(sqlite3 *sqlite, const char *database, int reposcver)
+
{
+
	int		 retcode = EPKG_OK;
+
	char		 sql[BUFSIZ];
+
	char		*errmsg;
+
	const char	*fmt = "PRAGMA %Q.user_version = %" PRId64 ";" ;
+

+
	assert(database != NULL);
+

+
	sqlite3_snprintf(sizeof(sql), sql, fmt, database, reposcver);
+

+
	if (sqlite3_exec(sqlite, sql, NULL, NULL, &errmsg) != SQLITE_OK) {
+
		pkg_emit_error("sqlite: %s", errmsg);
+
		sqlite3_free(errmsg);
+
		retcode = EPKG_FATAL;
+
	}
+
	return (retcode);
+
}
+

+
static int
initialize_repo(const char *repodb, const char *filesdb, bool force,
    bool files, sqlite3 **sqlite)
{
@@ -316,85 +353,6 @@ initialize_repo(const char *repodb, const char *filesdb, bool force,
	int reposcver;
	int retcode = EPKG_OK;

-
	const char initsql[] = ""
-
		"CREATE TABLE packages ("
-
			"id INTEGER PRIMARY KEY,"
-
			"origin TEXT UNIQUE,"
-
			"name TEXT NOT NULL,"
-
			"version TEXT NOT NULL,"
-
			"comment TEXT NOT NULL,"
-
			"desc TEXT NOT NULL,"
-
			"osversion TEXT,"
-
			"arch TEXT NOT NULL,"
-
			"maintainer TEXT NOT NULL,"
-
			"www TEXT,"
-
			"prefix TEXT NOT NULL,"
-
			"pkgsize INTEGER NOT NULL,"
-
			"flatsize INTEGER NOT NULL,"
-
			"licenselogic INTEGER NOT NULL,"
-
			"cksum TEXT NOT NULL,"
-
			/* relative path to the package in the repository */
-
			"path TEXT NOT NULL,"
-
			"pkg_format_version INTEGER"
-
		");"
-
		"CREATE TABLE deps ("
-
			"origin TEXT,"
-
			"name TEXT,"
-
			"version TEXT,"
-
			"package_id INTEGER REFERENCES packages(id)"
-
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
-
			"UNIQUE(package_id, origin)"
-
		");"
-
		"CREATE TABLE categories ("
-
			"id INTEGER PRIMARY KEY, "
-
			"name TEXT NOT NULL UNIQUE "
-
		");"
-
		"CREATE TABLE pkg_categories ("
-
			"package_id INTEGER REFERENCES packages(id)"
-
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
-
			"category_id INTEGER REFERENCES categories(id)"
-
			"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
-
			"UNIQUE(package_id, category_id)"
-
		");"
-
		"CREATE TABLE licenses ("
-
			"id INTEGER PRIMARY KEY,"
-
			"name TEXT NOT NULL UNIQUE"
-
		");"
-
		"CREATE TABLE pkg_licenses ("
-
			"package_id INTEGER REFERENCES packages(id)"
-
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
-
			"license_id INTEGER REFERENCES licenses(id)"
-
			"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
-
			"UNIQUE(package_id, license_id)"
-
		");"
-
		"CREATE TABLE options ("
-
			"package_id INTEGER REFERENCES packages(id)"
-
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
-
			"option TEXT,"
-
			"value TEXT,"
-
			"UNIQUE (package_id, option)"
-
		");"
-
		"CREATE TABLE shlibs ("
-
			"id INTEGER PRIMARY KEY,"
-
			"name TEXT NOT NULL UNIQUE "
-
		");"
-
		"CREATE TABLE pkg_shlibs_required ("
-
			"package_id INTEGER NOT NULL REFERENCES packages(id)"
-
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
-
			"shlib_id INTEGER NOT NULL REFERENCES shlibs(id)"
-
			"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
-
			"UNIQUE(package_id, shlib_id)"
-
		");"
-
		"CREATE TABLE pkg_shlibs_provided ("
-
			"package_id INTEGER NOT NULL REFERENCES packages(id)"
-
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
-
			"shlib_id INTEGER NOT NULL REFERENCES shlibs(id)"
-
			"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
-
			"UNIQUE(package_id, shlib_id)"
-
		");"
-
		"PRAGMA user_version=%d;"
-
		;
-

	const char initfilessql[] = ""
		"CREATE TABLE files.files ("
			"path TEXT,"
@@ -1082,6 +1040,146 @@ pkg_finish_repo(char *path, pem_password_cb *password_cb, char *rsa_key_path)
	return (EPKG_OK);
}

+
/* We want to replace some arbitrary number of instances of the placeholder
+
   %Q in the SQL with the name of the database. */
+
static int
+
substitute_into_sql(char *sqlbuf, size_t buflen, const char *fmt,
+
		    const char *replacement)
+
{
+
	char	*f;
+
	char	*f0;
+
	char	*tofree;
+
	size_t	 len;
+
	int	 ret = EPKG_OK;
+

+
	tofree = f = strdup(fmt);
+
	if (tofree == NULL)
+
		return EPKG_FATAL; /* out of memory */
+

+
	sqlbuf[0] = '\0';
+

+
	while ((f0 = strsep(&f, "%")) != NULL) {
+
		len = strlcat(sqlbuf, f0, buflen);
+
		if (len >= buflen) {
+
			/* Overflowed the buffer */
+
			ret = EPKG_FATAL;
+
			break;
+
		}
+

+
		if (f[0] == 'Q') {
+
			len = strlcat(sqlbuf, replacement, buflen);
+
			f++;	/* Jump the Q */
+
		} else {
+
			len = strlcat(sqlbuf, "%", buflen);
+
		}
+

+
		if (len >= buflen) {
+
			/* Overflowed the buffer */
+
			ret = EPKG_FATAL;
+
			break;
+
		}
+
	}
+
	
+
	free(tofree);
+

+
	return (ret);
+
}
+

+

+
static int
+
apply_repo_change(struct pkgdb *db, const char *database,
+
		  struct repo_changes *repo_changes, const char *updown,
+
		  int version)
+
{
+
	struct repo_changes	*change;
+
	bool			 found = false;
+
	int			 ret = EPKG_OK;
+
	char			 sql[BUFSIZ];
+
	char			*errmsg;
+
	
+
        for (change = repo_changes; change->version != -1; change++) {
+
		if (change->version == version) {
+
			found = true;
+
			break;
+
		}
+
	}
+
	if (!found) {
+
		pkg_emit_error("Failed to %s \"%s\" repo schema to "
+
			" version %d (target version %d) "
+
			"-- change not found", updown, database, version,
+
			REPO_SCHEMA_VERSION);
+
		return (EPKG_FATAL);
+
	}
+

+
	/* substitute the repo database name */
+
	ret = substitute_into_sql(sql, sizeof(sql), change->sql, database);
+

+
	/* begin transaction */
+
	if (ret == EPKG_OK)
+
		ret = pkgdb_transaction_begin(db->sqlite, NULL);
+

+
	/* apply change */
+
	if (ret == EPKG_OK) {
+
		ret = sqlite3_exec(db->sqlite, sql, NULL, NULL, &errmsg);
+
		if (ret != SQLITE_OK) {
+
			pkg_emit_error("sqlite: %s", errmsg);
+
			sqlite3_free(errmsg);
+
			ret = EPKG_FATAL;
+
		}
+
	}
+
	
+
	/* update repo user_version */
+
	if (ret == EPKG_OK)
+
		ret = set_repo_user_version(db->sqlite, database, version);
+

+
	/* commit or rollback */
+
	if (ret == EPKG_OK)
+
		ret = pkgdb_transaction_commit(db->sqlite, NULL);
+
	else
+
		ret = pkgdb_transaction_rollback(db->sqlite, NULL);
+

+
	if (ret == EPKG_OK) {
+
		pkg_emit_notice("Repo %s schema %s to version %d: %s",
+
				database, version, change->message);
+
	}
+

+
	return (ret);
+
}
+

+
static int
+
upgrade_repo_schema(struct pkgdb *db, const char *database, int current_version)
+
{
+
	int version;
+
	int ret = EPKG_OK;
+

+
	for (version = current_version;
+
	     version <= REPO_SCHEMA_VERSION;
+
	     version++)  {
+
		ret = apply_repo_change(db, database, repo_upgrades,
+
					"upgrade", version);
+
		if (ret != EPKG_OK)
+
			break;
+
	}
+
	return (ret);
+
}
+

+
static int
+
downgrade_repo_schema(struct pkgdb *db, const char *database, int current_version)
+
{
+
	int version;
+
	int ret = EPKG_OK;
+

+
	for (version = current_version - 1;
+
	     version >= REPO_SCHEMA_VERSION;
+
	     version--)  {
+
		ret = apply_repo_change(db, database, repo_downgrades,
+
					"downgrade", version);
+
		if (ret != EPKG_OK)
+
			break;
+
	}
+
	return (ret);
+
}
+

int
pkg_check_repo_version(struct pkgdb *db, const char *database)
{
@@ -1109,31 +1207,58 @@ pkg_check_repo_version(struct pkgdb *db, const char *database)
	 *
	 * So long as the major versions are the same, the local pkgng
	 * should be compatible with any repo created by a more recent
-
	 * pkgng
+
	 * pkgng, although it may need some modification of the repo
+
	 * schema
	 */

-
	/* --- Temporary ---- Grandfather in the old repo schema version
-
	   so this patch doesn't immediately invalidate all the repos out there */
+
	/* --- Temporary ---- Grandfather in the old repo schema
+
	   version so this patch doesn't immediately invalidate all
+
	   the repos out there */
+

	if (reposcver == 2)
		reposcver = 2000;
	if (reposcver == 3)
		reposcver = 2001;

-
	if (reposcver > REPO_SCHEMA_VERSION) {
-
		pkg_emit_error("Repo %s (schema version %d) is too new - we can"
-
		    " accept at most version %d", database, reposcver,
-
		    REPO_SCHEMA_VERSION);
-
		return (EPKG_REPOSCHEMA);
-
	}
-
	
	repomajor = reposcver / 1000;

	if (repomajor < REPO_SCHEMA_MAJOR) {
		pkg_emit_error("Repo %s (schema version %d) is too old - "
		    "need at least schema %d", database, reposcver,
		    REPO_SCHEMA_MAJOR * 1000);
-
//		return (EPKG_REPOSCHEMA);
+
		return (EPKG_REPOSCHEMA);
	}

-
	return (EPKG_OK);
+
	if (repomajor > REPO_SCHEMA_MAJOR) {
+
		pkg_emit_error("Repo %s (schema version %d) is too new - "
+
		    "we can accept at most schema %d", database, reposcver,
+
		    ((REPO_SCHEMA_MAJOR + 1) * 1000) - 1);
+
		return (EPKG_REPOSCHEMA);
+
	}
+

+
	/* This is a repo schema version we can work with */
+

+
	ret = EPKG_OK;
+

+
	if (reposcver > REPO_SCHEMA_VERSION) {
+
		if (sqlite3_db_readonly(db->sqlite, database)) {
+
			pkg_emit_error("Repo %s needs schema upgrade from "
+
			"%d to %d but it is opened readonly", database,
+
			       reposcver, REPO_SCHEMA_VERSION
+
			);
+
			ret = EPKG_FATAL;
+
		} else
+
			ret = upgrade_repo_schema(db, database, reposcver);
+
	} else if (reposcver < REPO_SCHEMA_VERSION) {
+
		if (sqlite3_db_readonly(db->sqlite, database)) {
+
			pkg_emit_error("Repo %s needs schema downgrade from "
+
			"%d to %d but it is opened readonly", database,
+
			       reposcver, REPO_SCHEMA_VERSION
+
			);
+
			ret = EPKG_FATAL;
+
		} else
+
			ret = downgrade_repo_schema(db, database, reposcver);
+
	}
+

+
	return (ret);
}
modified libpkg/pkgdb.c
@@ -587,6 +587,19 @@ pkgdb_init(sqlite3 *sdb)
			" ON DELETE RESTRICT ON UPDATE RESTRICT,"
		"UNIQUE (package_id, shlib_id)"
	");"
+
	"CREATE TABLE abstract ("
+
                "abstract_id INTEGER PRIMARY KEY,"
+
                "abstract TEXT NOT NULL UNIQUE"
+
        ");"
+
        "CREATE TABLE pkg_abstract ("
+
                "package_id INTERGER REFERENCES packages(id)"
+
                      " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
                "key_id INTEGER NOT NULL REFERENCES abstract(abstract_id)"
+
                      " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
		"value_id INTEGER NOT NULL REFERENCES abstract(abstract_id)"
+
		      " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
		"UNIQUE (package_id, key_id, value_id)"
+
	");"

	/* Mark the end of the array */

@@ -603,6 +616,7 @@ pkgdb_init(sqlite3 *sdb)
	"CREATE INDEX pkg_shlibs_required_package_id ON pkg_shlibs_required (package_id);"
	"CREATE INDEX pkg_shlibs_provided_package_id ON pkg_shlibs_provided (package_id);"
	"CREATE INDEX pkg_directories_directory_id ON pkg_directories (directory_id);"
+
	"CREATE INDEX pkg_abstract_package_id ON pkg_abstract(package_id);"

	"PRAGMA user_version = %d;"
	"COMMIT;"
@@ -843,7 +857,7 @@ pkgdb_access(unsigned mode, unsigned database)
	 *             (if $INSTALL_AS_USER is set) the current euid
	 *             and egid
	 *
-
	 * EPKG_ENOACCESS: we don't have privileges to read or write a
+
	 * EPKG_ENOACCESS: we don't have privileges to read or write
	 *
	 * EPKG_FATAL: Couldn't determine the answer for other reason,
	 *     like configuration screwed up, invalid argument values,
modified libpkg/private/db_upgrades.h
@@ -279,13 +279,28 @@ static struct db_upgrades {
		"shlib_id INTEGER NOT NULL REFERENCES shlibs(id)"
			" ON DELETE RESTRICT ON UPDATE RESTRICT,"
		"UNIQUE (package_id, shlib_id)"
-
	 ");"
-
	 "INSERT INTO pkg_shlibs_required (package_id, shlib_id)"
+
	");"
+
	"INSERT INTO pkg_shlibs_required (package_id, shlib_id)"
	 	" SELECT package_id, shlib_id FROM pkg_shlibs;"
-
	 "CREATE INDEX pkg_shlibs_required_package_id ON pkg_shlibs_required (package_id);"
-
	 "CREATE INDEX pkg_shlibs_provided_package_id ON pkg_shlibs_provided (package_id);"
-
	 "DROP INDEX pkg_shlibs_package_id;"
-
	 "DROP TABLE pkg_shlibs;"
+
	"CREATE INDEX pkg_shlibs_required_package_id ON pkg_shlibs_required (package_id);"
+
	"CREATE INDEX pkg_shlibs_provided_package_id ON pkg_shlibs_provided (package_id);"
+
	"DROP INDEX pkg_shlibs_package_id;"
+
	"DROP TABLE pkg_shlibs;"
+
	},
+
	{15,
+
	"CREATE TABLE abstract ("
+
                "abstract_id INTEGER PRIMARY KEY,"
+
                "abstract TEXT NOT NULL UNIQUE"
+
        ");"
+
        "CREATE TABLE pkg_abstract ("
+
                "package_id INTERGER REFERENCES packages(id)"
+
                      " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
                "key_id INTEGER NOT NULL REFERENCES abstract(abstract_id)"
+
                      " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
		"value_id INTEGER NOT NULL REFERENCES abstract(abstract_id)"
+
		      " ON DELETE CASCADE ON UPDATE RESTRICT"
+
	");"
+
	"CREATE INDEX pkg_abstract_package_id ON pkg_abstract(package_id);"
	},

	/* Mark the end of the array */
modified libpkg/private/event.h
@@ -29,6 +29,7 @@
#define _PKG_EVENT

void pkg_emit_error(const char *fmt, ...);
+
void pkg_emit_notice(const char *fmt, ...);
void pkg_emit_errno(const char *func, const char *arg);
void pkg_emit_already_installed(struct pkg *p);
void pkg_emit_fetching(const char *url, off_t total, off_t done, time_t elapsed);
modified libpkg/private/pkg.h
@@ -119,6 +119,7 @@ struct pkg {
	struct pkg_group *groups;
	struct pkg_shlib *shlibs_required;
	struct pkg_shlib *shlibs_provided;
+
	struct pkg_abstract *abstract_metadata;
	unsigned       	 flags;
	int64_t		 rowid;
	int64_t		 time;
@@ -244,6 +245,12 @@ struct pkg_config_value {
	UT_hash_handle hh;
};

+
struct pkg_abstract {
+
	struct sbuf	*key;
+
	struct sbuf	*value;
+
	UT_hash_handle	 hh;
+
};
+

/* sql helpers */

typedef struct _sql_prstmt {
added libpkg/private/repodb.h
@@ -0,0 +1,212 @@
+
/*-
+
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
+
 * Copyright (c) 2013 Matthew Seaman <matthew@FreeBSD.org>
+
 * All rights reserved.
+
 * 
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions
+
 * are met:
+
 * 1. Redistributions of source code must retain the above copyright
+
 *    notice, this list of conditions and the following disclaimer
+
 *    in this position and unchanged.
+
 * 2. Redistributions in binary form must reproduce the above copyright
+
 *    notice, this list of conditions and the following disclaimer in the
+
 *    documentation and/or other materials provided with the distribution.
+
 * 
+
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

+
#ifndef _REPODB
+
#define _REPODB
+

+
static const char initsql[] = ""
+
	"CREATE TABLE packages ("
+
	    "id INTEGER PRIMARY KEY,"
+
	    "origin TEXT UNIQUE,"
+
	    "name TEXT NOT NULL,"
+
	    "version TEXT NOT NULL,"
+
	    "comment TEXT NOT NULL,"
+
	    "desc TEXT NOT NULL,"
+
	    "osversion TEXT,"
+
	    "arch TEXT NOT NULL,"
+
	    "maintainer TEXT NOT NULL,"
+
	    "www TEXT,"
+
	    "prefix TEXT NOT NULL,"
+
	    "pkgsize INTEGER NOT NULL,"
+
	    "flatsize INTEGER NOT NULL,"
+
	    "licenselogic INTEGER NOT NULL,"
+
	    "cksum TEXT NOT NULL,"
+
	    /* relative path to the package in the repository */
+
	    "path TEXT NOT NULL,"
+
	    "pkg_format_version INTEGER"
+
	");"
+
	"CREATE TABLE deps ("
+
			"origin TEXT,"
+
			"name TEXT,"
+
			"version TEXT,"
+
			"package_id INTEGER REFERENCES packages(id)"
+
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
			"UNIQUE(package_id, origin)"
+
	");"
+
	"CREATE TABLE categories ("
+
			"id INTEGER PRIMARY KEY, "
+
			"name TEXT NOT NULL UNIQUE "
+
	");"
+
	"CREATE TABLE pkg_categories ("
+
			"package_id INTEGER REFERENCES packages(id)"
+
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
			"category_id INTEGER REFERENCES categories(id)"
+
			"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
			"UNIQUE(package_id, category_id)"
+
	");"
+
	"CREATE TABLE licenses ("
+
			"id INTEGER PRIMARY KEY,"
+
			"name TEXT NOT NULL UNIQUE"
+
	");"
+
	"CREATE TABLE pkg_licenses ("
+
			"package_id INTEGER REFERENCES packages(id)"
+
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
			"license_id INTEGER REFERENCES licenses(id)"
+
			"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
			"UNIQUE(package_id, license_id)"
+
	");"
+
	"CREATE TABLE options ("
+
			"package_id INTEGER REFERENCES packages(id)"
+
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
			"option TEXT,"
+
			"value TEXT,"
+
			"UNIQUE (package_id, option)"
+
	");"
+
	"CREATE TABLE shlibs ("
+
			"id INTEGER PRIMARY KEY,"
+
			"name TEXT NOT NULL UNIQUE "
+
	");"
+
	"CREATE TABLE pkg_shlibs_required ("
+
			"package_id INTEGER NOT NULL REFERENCES packages(id)"
+
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
			"shlib_id INTEGER NOT NULL REFERENCES shlibs(id)"
+
			"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
			"UNIQUE(package_id, shlib_id)"
+
	");"
+
	"CREATE TABLE pkg_shlibs_provided ("
+
			"package_id INTEGER NOT NULL REFERENCES packages(id)"
+
		        "  ON DELETE CASCADE ON UPDATE CASCADE,"
+
			"shlib_id INTEGER NOT NULL REFERENCES shlibs(id)"
+
			"  ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
			"UNIQUE(package_id, shlib_id)"
+
	");"
+
	"CREATE TABLE abstract ("
+
                        "abstract_id INTEGER PRIMARY KEY,"
+
                        "abstract TEXT NOT NULL UNIQUE"
+
	");"
+
	"CREATE TABLE pkg_abstract ("
+
                        "package_id INTERGER REFERENCES packages(id)"
+
                        " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
                        "key_id INTEGER NOT NULL REFERENCES abstract(abstract_id)"
+
                        " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
		        "value_id INTEGER NOT NULL REFERENCES abstract(abstract_id)"
+
		        " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
		        "UNIQUE (package_id, key_id, value_id)"
+
	");"
+
	"PRAGMA user_version=%d;"
+
	;
+

+
struct repo_changes {
+
	int version;		/* The repo schema version this changes to */
+
	const char *message;
+
	const char *sql;
+
};
+

+
/* How to upgrade an older repo to match what the current system
+
   expects */
+
static struct repo_changes repo_upgrades[] = {
+
	{2002,
+
	 "Modify shlibs to add \'provided\' as well as \'required\': Use \'pkg create -Ba\' to initialise shlibs provided if desired",
+
	 "CREATE TABLE %Q.pkg_shlibs_required ("
+
		"package_id INTEGER NOT NULL REFERENCES %Q.packages(id)"
+
			" ON DELETE CASCADE ON UPDATE CASCADE,"
+
		"shlib_id INTEGER NOT NULL REFERENCES %Q.shlibs(id)"
+
			" ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
		"UNIQUE (package_id, shlib_id)"
+
	 ");"
+
	 "CREATE TABLE %Q.pkg_shlibs_provided ("
+
		"package_id INTEGER NOT NULL REFERENCES %Q.packages(id)"
+
			" ON DELETE CASCADE ON UPDATE CASCADE,"
+
		"shlib_id INTEGER NOT NULL REFERENCES %Q.shlibs(id)"
+
			" ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
		"UNIQUE (package_id, shlib_id)"
+
	 ");"
+
	 "INSERT INTO %Q.pkg_shlibs_required (package_id, shlib_id)"
+
	 	" SELECT package_id, shlib_id FROM %Q.pkg_shlibs;"
+
	 "DROP TABLE %Q.pkg_shlibs;"
+
	},
+
	{2003,
+
	 "Add abstract metadata", 
+
	 "CREATE TABLE %Q.abstract ("
+
                "abstract_id INTEGER PRIMARY KEY,"
+
                "abstract TEXT NOT NULL UNIQUE"
+
	 ");"
+
	 "CREATE TABLE %Q.pkg_abstract ("
+
                "package_id INTERGER REFERENCES %Q.packages(id)"
+
                      " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
                "key_id INTEGER NOT NULL REFERENCES %Q.abstract(abstract_id)"
+
                      " ON DELETE CASCADE ON UPDATE RESTRICT,"
+
		"value_id INTEGER NOT NULL REFERENCES %Q.abstract(abstract_id)"
+
		      " ON DELETE CASCADE ON UPDATE RESTRICT"
+
	 ");"
+
	},
+

+
	/* Mark the end of the array */
+
	{ -1, NULL, NULL, },
+

+
};
+

+
/* How to downgrade a newer repo to match what the current system
+
   expects */
+
static struct repo_changes repo_downgrades[] = {
+
	{2002,
+
	 "New abstract metadata", 
+
	 "DROP TABLE %Q.pkg_abstract;"
+
	 "DROP TABLE %Q.abstract;"
+
	},
+
	{2001,
+
	 "Update shlibs add provided as well as required",
+
	 "CREATE TABLE %Q.pkg_shlibs_required ("
+
		"package_id INTEGER NOT NULL REFERENCES %Q.packages(id)"
+
			" ON DELETE CASCADE ON UPDATE CASCADE,"
+
		"shlib_id INTEGER NOT NULL REFERENCES %Q.shlibs(id)"
+
			" ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
		"UNIQUE (package_id, shlib_id)"
+
	 ");"
+
	 "CREATE TABLE %Q.pkg_shlibs ("
+
                "package_id INTEGER REFERENCES %Q.packages(id)"
+
	                " ON DELETE CASCADE ON UPDATE CASCADE,"
+
                "shlib_id INTEGER REFERENCES %Q.shlibs(id)"
+
                        " ON DELETE RESTRICT ON UPDATE RESTRICT,"
+
	        "PRIMARY KEY (package_id, shlib_id)"
+
	 ");"
+
	 "INSERT INTO %Q.pkg_shlibs (package_id, shlib_id)"
+
	        " SELECT package_id, shlib_id FROM %Q.pkg_shlibs_required;"
+
	 "DELETE FROM %Q.shlibs WHERE id NOT IN"
+
	        " (SELECT shlib_id FROM %Q.pkg_shlibs);"
+
	 "DROP TABLE %Q.pkg_shlibs_provided;"
+
	 "DROP TABLE %Q.pkg_shlibs_required;"
+
	},
+

+

+
	/* Mark the end of the array */
+
	{ -1, NULL, NULL, },
+

+
};
+

+
#endif	/* _REPODB */