From 80e66d2b321669913857e99b70dfba7e6168d3b9 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Tue, 13 Nov 2018 00:58:44 +0000 Subject: [PATCH] Initial sqlite3 compatibility --- activitypub.go | 33 ++++++-------- app.go | 9 +++- config/config.go | 5 +-- config/setup.go | 48 +------------------- database.go | 114 +++++++++++++++++++++++------------------------ nodeinfo.go | 9 +++- schema.sql | 75 +++++++++++++++---------------- 7 files changed, 123 insertions(+), 170 deletions(-) diff --git a/activitypub.go b/activitypub.go index 60561af..e7a564d 100644 --- a/activitypub.go +++ b/activitypub.go @@ -7,7 +7,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/go-sql-driver/mysql" "github.com/gorilla/mux" "github.com/writeas/activity/streams" "github.com/writeas/httpsig" @@ -344,12 +343,10 @@ func handleFetchCollectionInbox(app *app, w http.ResponseWriter, r *http.Request // Add follower locally, since it wasn't found before res, err := t.Exec("INSERT INTO remoteusers (actor_id, inbox, shared_inbox) VALUES (?, ?, ?)", fullActor.ID, fullActor.Inbox, fullActor.Endpoints.SharedInbox) if err != nil { - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number != mySQLErrDuplicateKey { - t.Rollback() - log.Error("Couldn't add new remoteuser in DB: %v\n", err) - return - } + if isConstraintError(err) { + t.Rollback() + log.Error("Couldn't add new remoteuser in DB: %v\n", err) + return } else { t.Rollback() log.Error("Couldn't add new remoteuser in DB: %v\n", err) @@ -367,12 +364,10 @@ func handleFetchCollectionInbox(app *app, w http.ResponseWriter, r *http.Request // Add in key _, err = t.Exec("INSERT INTO remoteuserkeys (id, remote_user_id, public_key) VALUES (?, ?, ?)", fullActor.PublicKey.ID, followerID, fullActor.PublicKey.PublicKeyPEM) if err != nil { - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number != mySQLErrDuplicateKey { - t.Rollback() - log.Error("Couldn't add follower keys in DB: %v\n", err) - return - } + if isConstraintError(err) { + t.Rollback() + log.Error("Couldn't add follower keys in DB: %v\n", err) + return } else { t.Rollback() log.Error("Couldn't add follower keys in DB: %v\n", err) @@ -382,14 +377,12 @@ func handleFetchCollectionInbox(app *app, w http.ResponseWriter, r *http.Request } // Add follow - _, err = t.Exec("INSERT INTO remotefollows (collection_id, remote_user_id, created) VALUES (?, ?, NOW())", c.ID, followerID) + _, err = t.Exec("INSERT INTO remotefollows (collection_id, remote_user_id, created) VALUES (?, ?, "+sqlNow+")", c.ID, followerID) if err != nil { - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number != mySQLErrDuplicateKey { - t.Rollback() - log.Error("Couldn't add follower in DB: %v\n", err) - return - } + if isConstraintError(err) { + t.Rollback() + log.Error("Couldn't add follower in DB: %v\n", err) + return } else { t.Rollback() log.Error("Couldn't add follower in DB: %v\n", err) diff --git a/app.go b/app.go index c50d0db..0ef9146 100644 --- a/app.go +++ b/app.go @@ -4,7 +4,7 @@ import ( "database/sql" "flag" "fmt" - _ "github.com/go-sql-driver/mysql" + _ "github.com/mattn/go-sqlite3" "html/template" "net/http" "os" @@ -258,7 +258,12 @@ func Serve() { func connectToDatabase(app *app) { log.Info("Connecting to database...") - db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true", app.cfg.Database.User, app.cfg.Database.Password, app.cfg.Database.Host, app.cfg.Database.Port, app.cfg.Database.Database)) + + if app.cfg.Database.Type != "sqlite3" { + log.Error("This fork of writefreely only supports sqlite3 databases") + os.Exit(1) + } + db, err := sql.Open("sqlite3", app.cfg.Database.Database) if err != nil { log.Error("%s", err) os.Exit(1) diff --git a/config/config.go b/config/config.go index c3c0628..3dfb7c2 100644 --- a/config/config.go +++ b/config/config.go @@ -59,9 +59,8 @@ func New() *Config { Port: 8080, }, Database: DatabaseCfg{ - Type: "mysql", - Host: "localhost", - Port: 3306, + Type: "sqlite3", + Database: "writefreely.sqlite3", }, App: AppCfg{ Host: "http://localhost:8080", diff --git a/config/setup.go b/config/setup.go index 54fa961..853b04f 100644 --- a/config/setup.go +++ b/config/setup.go @@ -65,30 +65,7 @@ func Configure() (*SetupData, error) { prompt = promptui.Prompt{ Templates: tmpls, - Label: "Username", - Validate: validateNonEmpty, - Default: data.Config.Database.User, - } - data.Config.Database.User, err = prompt.Run() - if err != nil { - return data, err - } - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Password", - Validate: validateNonEmpty, - Default: data.Config.Database.Password, - Mask: '*', - } - data.Config.Database.Password, err = prompt.Run() - if err != nil { - return data, err - } - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Database name", + Label: "Database filename", Validate: validateNonEmpty, Default: data.Config.Database.Database, } @@ -97,29 +74,6 @@ func Configure() (*SetupData, error) { return data, err } - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Host", - Validate: validateNonEmpty, - Default: data.Config.Database.Host, - } - data.Config.Database.Host, err = prompt.Run() - if err != nil { - return data, err - } - - prompt = promptui.Prompt{ - Templates: tmpls, - Label: "Port", - Validate: validatePort, - Default: fmt.Sprintf("%d", data.Config.Database.Port), - } - dbPort, err := prompt.Run() - if err != nil { - return data, err - } - data.Config.Database.Port, _ = strconv.Atoi(dbPort) // Ignore error, as we've already validated number - fmt.Println() title(" App setup ") fmt.Println() diff --git a/database.go b/database.go index a032a8e..684ccaf 100644 --- a/database.go +++ b/database.go @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/go-sql-driver/mysql" + "github.com/mattn/go-sqlite3" "github.com/guregu/null" "github.com/guregu/null/zero" uuid "github.com/nu7hatch/gouuid" @@ -22,10 +22,19 @@ import ( "github.com/writeas/writefreely/author" ) -const ( - mySQLErrDuplicateKey = 1062 +var ( + sqlNow = `datetime('now')` + sqlExpires = `(expires IS NULL OR expires > `+sqlNow+`)` ) +func isConstraintError(err error) bool { + if sqliteErr, ok := err.(*sqlite3.Error); ok { + return sqliteErr.Code == sqlite3.ErrConstraint + } + + return false +} + type writestore interface { CreateUser(*User, string) error UpdateUserEmail(keys *keychain, userID int64, email string) error @@ -110,13 +119,11 @@ func (db *datastore) CreateUser(u *User, collectionTitle string) error { // 1. Add to `users` table // NOTE: Assumes User's Password is already hashed! - res, err := t.Exec("INSERT INTO users (username, password, email, created) VALUES (?, ?, ?, NOW())", u.Username, u.HashedPass, u.Email) + res, err := t.Exec("INSERT INTO users (username, password, email, created) VALUES (?, ?, ?, "+sqlNow+")", u.Username, u.HashedPass, u.Email) if err != nil { t.Rollback() - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number == mySQLErrDuplicateKey { - return impart.HTTPError{http.StatusConflict, "Username is already taken."} - } + if isConstraintError(err) { + return impart.HTTPError{http.StatusConflict, "Username is already taken."} } log.Error("Rolling back users INSERT: %v\n", err) @@ -136,10 +143,8 @@ func (db *datastore) CreateUser(u *User, collectionTitle string) error { res, err = t.Exec("INSERT INTO collections (alias, title, description, privacy, owner_id, view_count) VALUES (?, ?, ?, ?, ?, ?)", u.Username, collectionTitle, "", CollUnlisted, u.ID, 0) if err != nil { t.Rollback() - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number == mySQLErrDuplicateKey { - return impart.HTTPError{http.StatusConflict, "Username is already taken."} - } + if isConstraintError(err) { + return impart.HTTPError{http.StatusConflict, "Username is already taken."} } log.Error("Rolling back collections INSERT: %v\n", err) return err @@ -208,10 +213,8 @@ func (db *datastore) CreateCollection(alias, title string, userID int64) (*Colle // All good, so create new collection res, err := db.Exec("INSERT INTO collections (alias, title, description, privacy, owner_id, view_count) VALUES (?, ?, ?, ?, ?, ?)", alias, title, "", CollUnlisted, userID, 0) if err != nil { - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number == mySQLErrDuplicateKey { - return nil, impart.HTTPError{http.StatusConflict, "Collection already exists."} - } + if isConstraintError(err) { + return nil, impart.HTTPError{http.StatusConflict, "Collection already exists."} } log.Error("Couldn't add to collections: %v\n", err) return nil, err @@ -329,7 +332,7 @@ func (db *datastore) GetUserNameFromToken(accessToken string) (string, error) { var oneTime bool var username string - err := db.QueryRow("SELECT username, one_time FROM accesstokens LEFT JOIN users ON user_id = id WHERE token = ? AND (expires IS NULL OR expires > NOW())", t).Scan(&username, &oneTime) + err := db.QueryRow("SELECT username, one_time FROM accesstokens LEFT JOIN users ON user_id = id WHERE token = ? AND "+sqlExpires, t).Scan(&username, &oneTime) switch { case err == sql.ErrNoRows: return "", ErrBadAccessToken @@ -354,7 +357,7 @@ func (db *datastore) GetUserDataFromToken(accessToken string) (int64, string, er var userID int64 var oneTime bool var username string - err := db.QueryRow("SELECT user_id, username, one_time FROM accesstokens LEFT JOIN users ON user_id = id WHERE token = ? AND (expires IS NULL OR expires > NOW())", t).Scan(&userID, &username, &oneTime) + err := db.QueryRow("SELECT user_id, username, one_time FROM accesstokens LEFT JOIN users ON user_id = id WHERE token = ? AND "+sqlExpires, t).Scan(&userID, &username, &oneTime) switch { case err == sql.ErrNoRows: return 0, "", ErrBadAccessToken @@ -393,7 +396,7 @@ func (db *datastore) GetUserIDPrivilege(accessToken string) (userID int64, sudo } var oneTime bool - err := db.QueryRow("SELECT user_id, sudo, one_time FROM accesstokens WHERE token = ? AND (expires IS NULL OR expires > NOW())", t).Scan(&userID, &sudo, &oneTime) + err := db.QueryRow("SELECT user_id, sudo, one_time FROM accesstokens WHERE token = ? AND "+sqlExpires, t).Scan(&userID, &sudo, &oneTime) switch { case err == sql.ErrNoRows: return -1, false @@ -425,7 +428,7 @@ func (db *datastore) DeleteToken(accessToken []byte) error { // userID. func (db *datastore) FetchLastAccessToken(userID int64) string { var t []byte - err := db.QueryRow("SELECT token FROM accesstokens WHERE user_id = ? AND (expires IS NULL OR expires > NOW()) ORDER BY created DESC LIMIT 1", userID).Scan(&t) + err := db.QueryRow("SELECT token FROM accesstokens WHERE user_id = ? AND "+sqlExpires+" ORDER BY created DESC LIMIT 1", userID).Scan(&t) switch { case err == sql.ErrNoRows: return "" @@ -468,12 +471,16 @@ func (db *datastore) GetTemporaryOneTimeAccessToken(userID int64, validSecs int, // Insert UUID to `accesstokens` binTok := u[:] - expirationVal := "NULL" + now := time.Now() + expirationVal := interface{}("NULL") + if validSecs > 0 { - expirationVal = fmt.Sprintf("DATE_ADD(NOW(), INTERVAL %d SECOND)", validSecs) + expirationVal = interface{}(now.Add(time.Duration(validSecs)*time.Second)) } - _, err = db.Exec("INSERT INTO accesstokens (token, user_id, created, one_time, expires) VALUES (?, ?, NOW(), ?, "+expirationVal+")", string(binTok), userID, oneTime) + + + _, err = db.Exec("INSERT INTO accesstokens (token, user_id, created, one_time, expires) VALUES (?, ?, ?, ?, ?)", string(binTok), userID, now, oneTime, expirationVal) if err != nil { log.Error("Couldn't INSERT accesstoken: %v", err) return "", err @@ -557,18 +564,16 @@ func (db *datastore) CreatePost(userID, collID int64, post *SubmittedPost) (*Pos } } - _, err = db.Exec("INSERT INTO posts (id, slug, title, content, text_appearance, language, rtl, owner_id, collection_id, updated, view_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?)", friendlyID, slug, post.Title, post.Content, appearance, post.Language, post.IsRTL, ownerID, ownerCollID, 0) + _, err = db.Exec("INSERT INTO posts (id, slug, title, content, text_appearance, language, rtl, owner_id, collection_id, updated, view_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, "+sqlNow+", ?)", friendlyID, slug, post.Title, post.Content, appearance, post.Language, post.IsRTL, ownerID, ownerCollID, 0) if err != nil { - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number == mySQLErrDuplicateKey { - // Duplicate entry error; try a new slug - // TODO: make this a little more robust - // TODO: reuse exact same db.Exec statement as above - slug = sql.NullString{id.GenSafeUniqueSlug(slug.String), true} - _, err = db.Exec("INSERT INTO posts (id, slug, title, content, text_appearance, language, rtl, owner_id, collection_id, updated, view_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), ?)", friendlyID, slug, post.Title, post.Content, appearance, post.Language, post.IsRTL, ownerID, ownerCollID, 0) - if err != nil { - return nil, handleFailedPostInsert(fmt.Errorf("Retried slug generation, still failed: %v", err)) - } + if isConstraintError(err) { + // Duplicate entry error; try a new slug + // TODO: make this a little more robust + // TODO: reuse exact same db.Exec statement as above + slug = sql.NullString{id.GenSafeUniqueSlug(slug.String), true} + _, err = db.Exec("INSERT INTO posts (id, slug, title, content, text_appearance, language, rtl, owner_id, collection_id, updated, view_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, "+sqlNow+", ?)", friendlyID, slug, post.Title, post.Content, appearance, post.Language, post.IsRTL, ownerID, ownerCollID, 0) + if err != nil { + return nil, handleFailedPostInsert(fmt.Errorf("Retried slug generation, still failed: %v", err)) } else { return nil, handleFailedPostInsert(err) } @@ -650,7 +655,7 @@ func (db *datastore) UpdateOwnedPost(post *AuthenticatedPost, userID int64) erro return ErrPostNoUpdatableVals } - queryUpdates += sep + "updated = NOW()" + queryUpdates += sep + "updated = " + sqlNow res, err := db.Exec("UPDATE posts SET "+queryUpdates+" WHERE id = ? AND "+authCondition, params...) if err != nil { @@ -962,7 +967,7 @@ func (db *datastore) GetPostsCount(c *CollectionObj, includeFuture bool) { var count int64 timeCondition := "" if !includeFuture { - timeCondition = "AND created <= NOW()" + timeCondition = "AND created <= " + sqlNow } err := db.QueryRow("SELECT COUNT(*) FROM posts WHERE collection_id = ? AND pinned_position IS NULL "+timeCondition, c.ID).Scan(&count) switch { @@ -1001,7 +1006,7 @@ func (db *datastore) GetPosts(c *Collection, page int, includeFuture bool) (*[]P } timeCondition := "" if !includeFuture { - timeCondition = "AND created <= NOW()" + timeCondition = "AND created <= " + sqlNow } rows, err := db.Query("SELECT "+postCols+" FROM posts WHERE collection_id = ? AND pinned_position IS NULL "+timeCondition+" ORDER BY created "+order+limitStr, collID) if err != nil { @@ -1058,7 +1063,7 @@ func (db *datastore) GetPostsTagged(c *Collection, tag string, page int, include } timeCondition := "" if !includeFuture { - timeCondition = "AND created <= NOW()" + timeCondition = "AND created <= " + sqlNow } rows, err := db.Query("SELECT "+postCols+" FROM posts WHERE collection_id = ? AND LOWER(content) RLIKE ? "+timeCondition+" ORDER BY created "+order+limitStr, collID, "#"+strings.ToLower(tag)+"[[:>:]]") if err != nil { @@ -1133,17 +1138,16 @@ func (db *datastore) CanCollect(cpr *ClaimPostRequest, userID int64) bool { func (db *datastore) AttemptClaim(p *ClaimPostRequest, query string, params []interface{}, slugIdx int) (sql.Result, error) { qRes, err := db.Exec(query, params...) if err != nil { - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number == mySQLErrDuplicateKey && slugIdx > -1 { - s := id.GenSafeUniqueSlug(p.Slug) - if s == p.Slug { - // Sanity check to prevent infinite recursion - return qRes, fmt.Errorf("GenSafeUniqueSlug generated nothing unique: %s", s) - } - p.Slug = s - params[slugIdx] = p.Slug - return db.AttemptClaim(p, query, params, slugIdx) + if isConstraintError(err) && slugIdx > -1 { + s := id.GenSafeUniqueSlug(p.Slug) + if s == p.Slug { + // Sanity check to prevent infinite recursion + return qRes, fmt.Errorf("GenSafeUniqueSlug generated nothing unique: %s", s) } + + p.Slug = s + params[slugIdx] = p.Slug + return db.AttemptClaim(p, query, params, slugIdx) } return qRes, fmt.Errorf("attemptClaim: %s", err) } @@ -1431,7 +1435,7 @@ func (db *datastore) GetLastPinnedPostPos(collID int64) int64 { } func (db *datastore) GetPinnedPosts(coll *CollectionObj) (*[]PublicPost, error) { - rows, err := db.Query("SELECT id, slug, title, LEFT(content, 80), pinned_position FROM posts WHERE collection_id = ? AND pinned_position IS NOT NULL ORDER BY pinned_position ASC", coll.ID) + rows, err := db.Query("SELECT id, slug, title, SUBSTR(content, 80), pinned_position FROM posts WHERE collection_id = ? AND pinned_position IS NOT NULL ORDER BY pinned_position ASC", coll.ID) if err != nil { log.Error("Failed selecting pinned posts: %v", err) return nil, impart.HTTPError{http.StatusInternalServerError, "Couldn't retrieve pinned posts."} @@ -1694,10 +1698,8 @@ func (db *datastore) ChangeSettings(app *app, u *User, s *userSettings) error { _, err = t.Exec("UPDATE users SET username = ? WHERE id = ?", newUsername, u.ID) if err != nil { t.Rollback() - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number == mySQLErrDuplicateKey { - return impart.HTTPError{http.StatusConflict, "Username is already taken."} - } + if isConstraintError(err) { + return impart.HTTPError{http.StatusConflict, "Username is already taken."} } log.Error("Unable to update users table: %v", err) return ErrInternalGeneral @@ -1706,10 +1708,8 @@ func (db *datastore) ChangeSettings(app *app, u *User, s *userSettings) error { _, err = t.Exec("UPDATE collections SET alias = ? WHERE alias = ? AND owner_id = ?", newUsername, u.Username, u.ID) if err != nil { t.Rollback() - if mysqlErr, ok := err.(*mysql.MySQLError); ok { - if mysqlErr.Number == mySQLErrDuplicateKey { - return impart.HTTPError{http.StatusConflict, "Username is already taken."} - } + if isConstraintError(err) { + return impart.HTTPError{http.StatusConflict, "Username is already taken."} } log.Error("Unable to update collection: %v", err) return ErrInternalGeneral diff --git a/nodeinfo.go b/nodeinfo.go index fd4b445..941e4d7 100644 --- a/nodeinfo.go +++ b/nodeinfo.go @@ -5,6 +5,7 @@ import ( "github.com/writeas/web-core/log" "github.com/writeas/writefreely/config" "strings" + "time" ) type nodeInfoResolver struct { @@ -67,6 +68,10 @@ func (r nodeInfoResolver) Usage() (nodeinfo.Usage, error) { log.Error("Unable to fetch post counts: %v", err) } + ago_1 := time.Now().Add(-(30*24*time.Hour)) + ago_6 := time.Now().Add(-(30*24*time.Hour*6)) + + if r.cfg.App.PublicStats { // Display bi-yearly / monthly stats err = r.db.QueryRow(`SELECT COUNT(*) FROM ( @@ -75,7 +80,7 @@ FROM posts INNER JOIN collections c ON collection_id = c.id WHERE collection_id IS NOT NULL - AND updated > DATE_SUB(NOW(), INTERVAL 6 MONTH)) co`).Scan(&activeHalfYear) + AND updated > ? co`, ago_6).Scan(&activeHalfYear) err = r.db.QueryRow(`SELECT COUNT(*) FROM ( SELECT DISTINCT collection_id @@ -83,7 +88,7 @@ FROM posts INNER JOIN FROM collections c ON collection_id = c.id WHERE collection_id IS NOT NULL - AND updated > DATE_SUB(NOW(), INTERVAL 1 MONTH)) co`).Scan(&activeMonth) + AND updated > ? co`, ago_1).Scan(&activeMonth) } return nodeinfo.Usage{ diff --git a/schema.sql b/schema.sql index b7716d7..f695d37 100644 --- a/schema.sql +++ b/schema.sql @@ -17,7 +17,7 @@ CREATE TABLE IF NOT EXISTS `accesstokens` ( `expires` datetime DEFAULT NULL, `user_agent` varchar(255) NOT NULL, PRIMARY KEY (`token`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +); -- -------------------------------------------------------- @@ -28,9 +28,9 @@ CREATE TABLE IF NOT EXISTS `accesstokens` ( CREATE TABLE IF NOT EXISTS `collectionattributes` ( `collection_id` int(6) NOT NULL, `attribute` varchar(128) NOT NULL, - `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `value` varchar(255) NOT NULL, PRIMARY KEY (`collection_id`,`attribute`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +); -- -------------------------------------------------------- @@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS `collectionkeys` ( `public_key` blob NOT NULL, `private_key` blob NOT NULL, PRIMARY KEY (`collection_id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +); -- -------------------------------------------------------- @@ -55,7 +55,7 @@ CREATE TABLE IF NOT EXISTS `collectionpasswords` ( `collection_id` int(6) NOT NULL, `password` char(60) NOT NULL, PRIMARY KEY (`collection_id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +); -- -------------------------------------------------------- @@ -67,7 +67,7 @@ CREATE TABLE IF NOT EXISTS `collectionredirects` ( `prev_alias` varchar(100) NOT NULL, `new_alias` varchar(100) NOT NULL, PRIMARY KEY (`prev_alias`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +); -- -------------------------------------------------------- @@ -76,19 +76,18 @@ CREATE TABLE IF NOT EXISTS `collectionredirects` ( -- CREATE TABLE IF NOT EXISTS `collections` ( - `id` int(6) NOT NULL AUTO_INCREMENT, - `alias` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, - `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, - `description` varchar(160) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `id` integer PRIMARY KEY ASC, + `alias` varchar(100) DEFAULT NULL, + `title` varchar(255) NOT NULL, + `description` varchar(160) NOT NULL, `style_sheet` text, - `script` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, + `script` text, `format` varchar(8) DEFAULT NULL, `privacy` tinyint(1) NOT NULL, `owner_id` int(6) NOT NULL, `view_count` int(6) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `alias` (`alias`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; + CONSTRAINT `alias` UNIQUE (`alias`) +); -- -------------------------------------------------------- @@ -106,17 +105,18 @@ CREATE TABLE IF NOT EXISTS `posts` ( `privacy` tinyint(1) NOT NULL, `owner_id` int(6) DEFAULT NULL, `collection_id` int(6) DEFAULT NULL, - `pinned_position` tinyint(1) UNSIGNED DEFAULT NULL, + `pinned_position` tinyint(1) DEFAULT NULL, `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated` timestamp NOT NULL, `view_count` int(6) NOT NULL, - `title` varchar(160) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, - `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `title` varchar(160) NOT NULL, + `content` text NOT NULL, PRIMARY KEY (`id`), - UNIQUE KEY `id_slug` (`collection_id`,`slug`), - UNIQUE KEY `owner_id` (`owner_id`,`id`), - KEY `privacy_id` (`privacy`,`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; + CONSTRAINT `id_slug` UNIQUE (`collection_id`,`slug`), + CONSTRAINT `owner_id` UNIQUE (`owner_id`,`id`) +-- FIXME: this is KEY `privacy_id` (`privacy`,`id`) in mysql +-- CONSTRAINT `privacy_id` (`privacy`,`id`) +); -- -------------------------------------------------------- @@ -129,7 +129,7 @@ CREATE TABLE IF NOT EXISTS `remotefollows` ( `remote_user_id` int(11) NOT NULL, `created` datetime NOT NULL, PRIMARY KEY (`collection_id`,`remote_user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +); -- -------------------------------------------------------- @@ -138,12 +138,11 @@ CREATE TABLE IF NOT EXISTS `remotefollows` ( -- CREATE TABLE IF NOT EXISTS `remoteuserkeys` ( - `id` varchar(255) NOT NULL, + `id` varchar(255) PRIMARY KEY NOT NULL, `remote_user_id` int(11) NOT NULL, `public_key` blob NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `follower_id` (`remote_user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; + CONSTRAINT `follower_id` UNIQUE (`remote_user_id`) +); -- -------------------------------------------------------- @@ -152,14 +151,13 @@ CREATE TABLE IF NOT EXISTS `remoteuserkeys` ( -- CREATE TABLE IF NOT EXISTS `remoteusers` ( - `id` int(11) NOT NULL AUTO_INCREMENT, + `id` integer PRIMARY KEY ASC, `actor_id` varchar(255) NOT NULL, `inbox` varchar(255) NOT NULL, `shared_inbox` varchar(255) NOT NULL, `followers` varchar(255) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `collection_id` (`actor_id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; + CONSTRAINT `collection_id` UNIQUE (`actor_id`) +); -- -------------------------------------------------------- @@ -168,11 +166,11 @@ CREATE TABLE IF NOT EXISTS `remoteusers` ( -- CREATE TABLE IF NOT EXISTS `userattributes` ( - `user_id` int(6) NOT NULL, + `user_id` integer NOT NULL, `attribute` varchar(64) NOT NULL, - `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `value` varchar(255) NOT NULL, PRIMARY KEY (`user_id`,`attribute`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +); -- -------------------------------------------------------- @@ -181,11 +179,10 @@ CREATE TABLE IF NOT EXISTS `userattributes` ( -- CREATE TABLE IF NOT EXISTS `users` ( - `id` int(6) NOT NULL AUTO_INCREMENT, - `username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, - `password` char(60) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, + `id` integer PRIMARY KEY ASC, + `username` varchar(100) NOT NULL, + `password` char(60) NOT NULL, `email` varbinary(255) DEFAULT NULL, `created` datetime NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `username` (`username`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; + CONSTRAINT `username` UNIQUE (`username`) +);