diff --git a/migrations/drivers.go b/migrations/drivers.go index 04b7c2a..1813615 100644 --- a/migrations/drivers.go +++ b/migrations/drivers.go @@ -1,218 +1,214 @@ /* * Copyright © 2019 Musing Studio LLC. * * This file is part of WriteFreely. * * WriteFreely is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, included * in the LICENSE file in this source code package. */ package migrations import ( "fmt" ) const INVALID_DRIVER_MSG = "Invalid database driver, check configuration file" // TODO: use now() from writefreely pkg func (db *datastore) now() string { switch db.driverName { case driverSQLite: return "strftime('%Y-%m-%d %H:%M:%S','now')" case driverMySQL, driverPostgres: return "NOW()" } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeInt() string { switch db.driverName { case driverSQLite, driverMySQL, driverPostgres: return "INTEGER" } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeSmallInt() string { switch db.driverName { case driverSQLite: return "INTEGER" case driverMySQL, driverPostgres: return "SMALLINT" } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeTinyInt() string { switch db.driverName { case driverSQLite: return "INTEGER" case driverMySQL: return "TINYINT" case driverPostgres: return "SMALLINT" } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeText() string { return "TEXT" } func (db *datastore) typeChar(l int) string { switch db.driverName { - case driverSQLite: + case driverSQLite, driverPostgres: return "TEXT" case driverMySQL: return fmt.Sprintf("CHAR(%d)", l) - case driverPostgres: - return fmt.Sprintf("CHAR(%d)", l) } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeVarChar(l int) string { switch db.driverName { - case driverSQLite: + case driverSQLite, driverPostgres: return "TEXT" case driverMySQL: return fmt.Sprintf("VARCHAR(%d)", l) - case driverPostgres: - return fmt.Sprintf("VARCHAR(%d)", l) } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeVarBinary(l int) string { switch db.driverName { case driverSQLite: return "BLOB" case driverMySQL: return fmt.Sprintf("VARBINARY(%d)", l) case driverPostgres: return "BYTEA" } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeBool() string { switch db.driverName { case driverSQLite: return "INTEGER" case driverMySQL: return "TINYINT(1)" case driverPostgres: return "BOOLEAN" } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeDateTime() string { switch db.driverName { case driverSQLite: return "DATETIME" case driverMySQL: return "DATETIME" case driverPostgres: return "TIMESTAMP" } panic(INVALID_DRIVER_MSG) } func (db *datastore) typeIntPrimaryKey() string { switch db.driverName { case driverSQLite: // From docs: "In SQLite, a column with type INTEGER PRIMARY KEY is an alias for the ROWID (except in WITHOUT // ROWID tables) which is always a 64-bit signed integer." return "INTEGER PRIMARY KEY" case driverMySQL: return "INT AUTO_INCREMENT PRIMARY KEY" case driverPostgres: return "INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY" } panic(INVALID_DRIVER_MSG) } func (db *datastore) collateMultiByte() string { if db.driverName == driverMySQL { return " COLLATE utf8_bin" } return "" } func (db *datastore) engine() string { if db.driverName == driverMySQL { return " ENGINE = InnoDB" } return "" } func (db *datastore) after(colName string) string { if db.driverName == driverMySQL { return fmt.Sprintf(" AFTER %s", colName) } return "" } func (db *datastore) boolTrue() string { return "TRUE" } func (db *datastore) boolFalse() string { return "FALSE" } func (db *datastore) limit(offset int, size int) string { switch db.driverName { case driverSQLite, driverMySQL: return fmt.Sprintf(" LIMIT %d, %d", offset, size) case driverPostgres: return fmt.Sprintf(" LIMIT %d OFFSET %d", size, offset) } panic(INVALID_DRIVER_MSG) } /** * MySQL/SQLite use '?' as placeholders in prepared statments * while PostgreSQL use '$1', '$2' ... '$n' instead * * this function converts a prepared statement in MySQL/SQLite * format into PostgreSQL format */ func (db *datastore) QueryWrap(q string) string { if db.driverName != driverPostgres { return q } output := "" escape := false ctr := 0 for i := range len(q) { if q[i] == '\'' || q[i] == '`' { escape = !escape } if q[i] == '?' && !escape { ctr += 1 output += fmt.Sprintf("$%d", ctr) } else { output += string(q[i]) } } return output } \ No newline at end of file diff --git a/postgres.sql b/postgres.sql index 1da5c1a..1b9b14d 100644 --- a/postgres.sql +++ b/postgres.sql @@ -1,148 +1,148 @@ CREATE TABLE IF NOT EXISTS "accesstokens" ( "token" BYTEA PRIMARY KEY, "user_id" INT NOT NULL, "sudo" BOOLEAN NOT NULL DEFAULT FALSE, "one_time" BOOLEAN NOT NULL DEFAULT FALSE, "created" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "expires" TIMESTAMP DEFAULT NULL, - "user_agent" VARCHAR(255) DEFAULT NULL + "user_agent" TEXT DEFAULT NULL ); CREATE TABLE IF NOT EXISTS "appcontent" ( - "id" VARCHAR(36) PRIMARY KEY, + "id" TEXT PRIMARY KEY, "content" TEXT NOT NULL, "updated" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE "appmigrations" ( "version" INT NOT NULL, "migrated" TIMESTAMP NOT NULL, "result" TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS "collectionattributes" ( "collection_id" INT NOT NULL, - "attribute" VARCHAR(128) NOT NULL, - "value" VARCHAR(255) NOT NULL, + "attribute" TEXT NOT NULL, + "value" TEXT NOT NULL, PRIMARY KEY ("collection_id", "attribute") ); CREATE TABLE IF NOT EXISTS "collectionkeys" ( "collection_id" INT PRIMARY KEY, "public_key" BYTEA NOT NULL, "private_key" BYTEA NOT NULL ); CREATE TABLE IF NOT EXISTS "collectionpasswords" ( "collection_id" INT PRIMARY KEY, - "password" CHAR(60) NOT NULL + "password" TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS "collectionredirects" ( - "prev_alias" VARCHAR(100) NOT NULL PRIMARY KEY, - "new_alias" VARCHAR(100) NOT NULL + "prev_alias" TEXT NOT NULL PRIMARY KEY, + "new_alias" TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS "collections" ( "id" SERIAL PRIMARY KEY, - "alias" VARCHAR(100) DEFAULT NULL, - "title" VARCHAR(255) NOT NULL, - "description" VARCHAR(160) NOT NULL, + "alias" TEXT DEFAULT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL, "style_sheet" TEXT, "script" TEXT DEFAULT NULL, - "format" VARCHAR(8) DEFAULT NULL, + "format" TEXT DEFAULT NULL, "privacy" INT NOT NULL, "owner_id" INT NOT NULL, "view_count" INT NOT NULL ); CREATE TABLE IF NOT EXISTS "posts" ( - "id" CHAR(16) PRIMARY KEY, - "slug" VARCHAR(100) DEFAULT NULL, - "modify_token" CHAR(32) DEFAULT NULL, - "text_appearance" CHAR(4) NOT NULL DEFAULT 'norm', - "language" CHAR(2) DEFAULT NULL, + "id" TEXT PRIMARY KEY, + "slug" TEXT DEFAULT NULL, + "modify_token" TEXT DEFAULT NULL, + "text_appearance" TEXT NOT NULL DEFAULT 'norm', + "language" TEXT DEFAULT NULL, "rtl" BOOLEAN DEFAULT NULL, "privacy" INT NOT NULL, "owner_id" INT DEFAULT NULL, "collection_id" INT DEFAULT NULL, "pinned_position" SMALLINT NULL, "created" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "updated" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "view_count" INT NOT NULL, - "title" VARCHAR(160) NOT NULL, + "title" TEXT NOT NULL, "content" TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS "remotefollows" ( "collection_id" INT NOT NULL, "remote_user_id" INT NOT NULL, "created" TIMESTAMP NOT NULL, PRIMARY KEY ("collection_id", "remote_user_id") ); CREATE TABLE IF NOT EXISTS "remoteuserkeys" ( - "id" VARCHAR(255) PRIMARY KEY, + "id" TEXT PRIMARY KEY, "remote_user_id" INT NOT NULL, "public_key" BYTEA NOT NULL ); CREATE TABLE IF NOT EXISTS "remoteusers" ( "id" SERIAL PRIMARY KEY, - "actor_id" VARCHAR(255) NOT NULL, - "inbox" VARCHAR(255) NOT NULL, - "shared_inbox" VARCHAR(255) NOT NULL + "actor_id" TEXT NOT NULL, + "inbox" TEXT NOT NULL, + "shared_inbox" TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS "userattributes" ( "user_id" INT NOT NULL, - "attribute" VARCHAR(64) NOT NULL, - "value" VARCHAR(255) NOT NULL, + "attribute" TEXT NOT NULL, + "value" TEXT NOT NULL, PRIMARY KEY ("user_id", "attribute") ); CREATE TABLE "userinvites" ( - "id" CHAR(6) NOT NULL, + "id" TEXT NOT NULL, "owner_id" INT NOT NULL, "max_uses" SMALLINT DEFAULT NULL, "created" TIMESTAMP NOT NULL, "expires" TIMESTAMP DEFAULT NULL, "inactive" BOOLEAN NOT NULL ); CREATE TABLE "users" ( "id" SERIAL PRIMARY KEY, - "username" VARCHAR(100) NOT NULL, - "password" CHAR(60) NOT NULL, + "username" TEXT NOT NULL, + "password" TEXT NOT NULL, "email" BYTEA DEFAULT NULL, "created" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE "usersinvited" ( - "invite_id" CHAR(6) NOT NULL, + "invite_id" TEXT NOT NULL, "user_id" INT NOT NULL ); CREATE INDEX IF NOT EXISTS "alias" ON "collections" ("alias"); CREATE UNIQUE INDEX IF NOT EXISTS "id_slug" ON "posts" ("collection_id", "slug"); CREATE UNIQUE INDEX IF NOT EXISTS "owner_id" ON "posts" ("owner_id", "id"); CREATE INDEX IF NOT EXISTS "privacy_id" ON "posts" ("privacy", "id"); CREATE UNIQUE INDEX IF NOT EXISTS "remote_user_id" ON "remoteuserkeys" ("remote_user_id"); CREATE UNIQUE INDEX IF NOT EXISTS "actor_id" ON "remoteusers" ("actor_id"); CREATE UNIQUE INDEX IF NOT EXISTS "username" ON "users" ("username");