Add description_name to clients

This commit is contained in:
Mike Schwörer 2024-06-01 01:01:58 +02:00
parent 7553e1f51e
commit 4c02afb957
Signed by: Mikescher
GPG Key ID: D3C7172E0A70F8CF
18 changed files with 889 additions and 155 deletions

View File

@ -32,3 +32,4 @@
- [ ] use sq.Query | sq.Update | sq.InsertAndQuery | .... - [ ] use sq.Query | sq.Update | sq.InsertAndQuery | ....
- [ ] sq.DBOptions - enable CommentTrimmer and DefaultConverter - [ ] sq.DBOptions - enable CommentTrimmer and DefaultConverter
- [ ] run unit-tests... - [ ] run unit-tests...
- [ ] Copy db.Migrate code

View File

@ -6,6 +6,7 @@ class Client {
final String timestampCreated; final String timestampCreated;
final String agentModel; final String agentModel;
final String agentVersion; final String agentVersion;
final String? descriptionName;
const Client({ const Client({
required this.clientID, required this.clientID,
@ -15,6 +16,7 @@ class Client {
required this.timestampCreated, required this.timestampCreated,
required this.agentModel, required this.agentModel,
required this.agentVersion, required this.agentVersion,
required this.descriptionName,
}); });
factory Client.fromJson(Map<String, dynamic> json) { factory Client.fromJson(Map<String, dynamic> json) {
@ -26,6 +28,7 @@ class Client {
timestampCreated: json['timestamp_created'] as String, timestampCreated: json['timestamp_created'] as String,
agentModel: json['agent_model'] as String, agentModel: json['agent_model'] as String,
agentVersion: json['agent_version'] as String, agentVersion: json['agent_version'] as String,
descriptionName: json['description_name'] as String?,
); );
} }

View File

@ -5,7 +5,7 @@
<file url="PROJECT" dialect="SQLite" /> <file url="PROJECT" dialect="SQLite" />
</component> </component>
<component name="SqlResolveMappings"> <component name="SqlResolveMappings">
<file url="file://$PROJECT_DIR$" scope="{&quot;node&quot;:{ &quot;@negative&quot;:&quot;1&quot;, &quot;group&quot;:{ &quot;@kind&quot;:&quot;root&quot;, &quot;node&quot;:{ &quot;name&quot;:{ &quot;@qname&quot;:&quot;b3228d61-4c36-41ce-803f-63bd80e198b3&quot; }, &quot;group&quot;:{ &quot;@kind&quot;:&quot;schema&quot;, &quot;node&quot;:{ &quot;name&quot;:{ &quot;@qname&quot;:&quot;schema_3.0.ddl&quot; } } } } } }}" /> <file url="file://$PROJECT_DIR$" scope="{&quot;node&quot;:{ &quot;@negative&quot;:&quot;1&quot;, &quot;group&quot;:{ &quot;@kind&quot;:&quot;root&quot;, &quot;node&quot;:{ &quot;name&quot;:{ &quot;@qname&quot;:&quot;787f0b19-0198-4890-b537-02b94cf8d411&quot; }, &quot;group&quot;:{ &quot;@kind&quot;:&quot;schema&quot;, &quot;node&quot;:{ &quot;name&quot;:{ &quot;@qname&quot;:&quot;main&quot; } } } } } }}" />
<file url="PROJECT" scope="" /> <file url="PROJECT" scope="" />
</component> </component>
</project> </project>

View File

@ -119,10 +119,11 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse {
UserID models.UserID `uri:"uid" binding:"entityid"` UserID models.UserID `uri:"uid" binding:"entityid"`
} }
type body struct { type body struct {
FCMToken string `json:"fcm_token" binding:"required"` FCMToken string `json:"fcm_token" binding:"required"`
AgentModel string `json:"agent_model" binding:"required"` AgentModel string `json:"agent_model" binding:"required"`
AgentVersion string `json:"agent_version" binding:"required"` AgentVersion string `json:"agent_version" binding:"required"`
ClientType models.ClientType `json:"client_type" binding:"required"` DescriptionName *string `json:"description_name"`
ClientType models.ClientType `json:"client_type" binding:"required"`
} }
var u uri var u uri
@ -147,7 +148,7 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete existing clients in db", err) return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete existing clients in db", err)
} }
client, err := h.database.CreateClient(ctx, u.UserID, clientType, b.FCMToken, b.AgentModel, b.AgentVersion) client, err := h.database.CreateClient(ctx, u.UserID, clientType, b.FCMToken, b.AgentModel, b.AgentVersion, b.DescriptionName)
if err != nil { if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err) return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err)
} }
@ -230,9 +231,10 @@ func (h APIHandler) UpdateClient(g *gin.Context) ginresp.HTTPResponse {
ClientID models.ClientID `uri:"cid" binding:"entityid"` ClientID models.ClientID `uri:"cid" binding:"entityid"`
} }
type body struct { type body struct {
FCMToken *string `json:"fcm_token"` FCMToken *string `json:"fcm_token"`
AgentModel *string `json:"agent_model"` AgentModel *string `json:"agent_model"`
AgentVersion *string `json:"agent_version"` AgentVersion *string `json:"agent_version"`
DescriptionName *string `json:"description_name"`
} }
var u uri var u uri
@ -282,6 +284,20 @@ func (h APIHandler) UpdateClient(g *gin.Context) ginresp.HTTPResponse {
} }
} }
if b.DescriptionName != nil {
if *b.DescriptionName == "" {
err = h.database.UpdateClientDescriptionName(ctx, u.ClientID, nil)
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update client", err)
}
} else {
err = h.database.UpdateClientDescriptionName(ctx, u.ClientID, langext.Ptr(*b.DescriptionName))
if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update client", err)
}
}
}
client, err = h.database.GetClient(ctx, u.UserID, u.ClientID) client, err = h.database.GetClient(ctx, u.UserID, u.ClientID)
if err != nil { if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) client", err) return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query (updated) client", err)

View File

@ -28,13 +28,14 @@ import (
// @Router /api/v2/users [POST] // @Router /api/v2/users [POST]
func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
type body struct { type body struct {
FCMToken string `json:"fcm_token"` FCMToken string `json:"fcm_token"`
ProToken *string `json:"pro_token"` ProToken *string `json:"pro_token"`
Username *string `json:"username"` Username *string `json:"username"`
AgentModel string `json:"agent_model"` AgentModel string `json:"agent_model"`
AgentVersion string `json:"agent_version"` AgentVersion string `json:"agent_version"`
ClientType models.ClientType `json:"client_type"` DescriptionName *string `json:"description_name"`
NoClient bool `json:"no_client"` ClientType models.ClientType `json:"client_type"`
NoClient bool `json:"no_client"`
} }
var b body var b body
@ -123,7 +124,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete existing clients in db", err) return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete existing clients in db", err)
} }
client, err := h.database.CreateClient(ctx, userobj.UserID, clientType, b.FCMToken, b.AgentModel, b.AgentVersion) client, err := h.database.CreateClient(ctx, userobj.UserID, clientType, b.FCMToken, b.AgentModel, b.AgentVersion, b.DescriptionName)
if err != nil { if err != nil {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err) return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err)
} }

View File

@ -206,7 +206,7 @@ func (h CompatHandler) Register(g *gin.Context) ginresp.HTTPResponse {
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create admin-key in db", err) return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create admin-key in db", err)
} }
_, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat") _, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat", nil)
if err != nil { if err != nil {
return ginresp.CompatAPIError(0, "Failed to create client in db") return ginresp.CompatAPIError(0, "Failed to create client in db")
} }
@ -661,7 +661,7 @@ func (h CompatHandler) Update(g *gin.Context) ginresp.HTTPResponse {
} }
_, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat") _, err = h.database.CreateClient(ctx, user.UserID, models.ClientTypeAndroid, *data.FCMToken, "compat", "compat", nil)
if err != nil { if err != nil {
return ginresp.CompatAPIError(0, "Failed to create client") return ginresp.CompatAPIError(0, "Failed to create client")
} }

View File

@ -5,55 +5,45 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/mattn/go-sqlite3" "github.com/mattn/go-sqlite3"
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
"gogs.mikescher.com/BlackForestBytes/goext/sq" "gogs.mikescher.com/BlackForestBytes/goext/sq"
"time" "time"
) )
func main() { func main() {
exerr.Init(exerr.ErrorPackageConfigInit{})
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
sqlite3.Version() // ensure slite3 loaded sqlite3.Version() // ensure slite3 loaded
{
h0, err := sq.HashSqliteSchema(ctx, schema.PrimarySchema[1].SQL) fmt.Println()
for i := 2; i <= schema.PrimarySchemaVersion; i++ {
h0, err := sq.HashMattnSqliteSchema(ctx, schema.PrimarySchema[i].SQL)
if err != nil { if err != nil {
h0 = "ERR" h0 = "ERR"
} }
fmt.Printf("PrimarySchema1 := %s\n", h0) fmt.Printf("PrimarySchema%d := %s\n", i, h0)
} }
{
h0, err := sq.HashSqliteSchema(ctx, schema.PrimarySchema[2].SQL) for i := 1; i <= schema.RequestsSchemaVersion; i++ {
h0, err := sq.HashMattnSqliteSchema(ctx, schema.RequestsSchema[i].SQL)
if err != nil { if err != nil {
h0 = "ERR" h0 = "ERR"
} }
fmt.Printf("PrimarySchema2 := %s\n", h0) fmt.Printf("RequestsSchema%d := %s\n", i, h0)
} }
{
h0, err := sq.HashSqliteSchema(ctx, schema.PrimarySchema[3].SQL) for i := 1; i <= schema.LogsSchemaVersion; i++ {
h0, err := sq.HashMattnSqliteSchema(ctx, schema.LogsSchema[i].SQL)
if err != nil { if err != nil {
h0 = "ERR" h0 = "ERR"
} }
fmt.Printf("PrimarySchema3 := %s\n", h0) fmt.Printf("LogsSchema%d := %s\n", i, h0)
}
{
h0, err := sq.HashSqliteSchema(ctx, schema.PrimarySchema[4].SQL)
if err != nil {
h0 = "ERR"
}
fmt.Printf("PrimarySchema4 := %s\n", h0)
}
{
h0, err := sq.HashSqliteSchema(ctx, schema.RequestsSchema[1].SQL)
if err != nil {
h0 = "ERR"
}
fmt.Printf("RequestsSchema1 := %s\n", h0)
}
{
h0, err := sq.HashSqliteSchema(ctx, schema.LogsSchema[1].SQL)
if err != nil {
h0 = "ERR"
}
fmt.Printf("LogsSchema1 := %s\n", h0)
} }
fmt.Println()
} }

View File

@ -7,7 +7,7 @@ import (
"time" "time"
) )
func (db *Database) CreateClient(ctx db.TxContext, userid models.UserID, ctype models.ClientType, fcmToken string, agentModel string, agentVersion string) (models.Client, error) { func (db *Database) CreateClient(ctx db.TxContext, userid models.UserID, ctype models.ClientType, fcmToken string, agentModel string, agentVersion string, descriptionName *string) (models.Client, error) {
tx, err := ctx.GetOrCreateTransaction(db) tx, err := ctx.GetOrCreateTransaction(db)
if err != nil { if err != nil {
return models.Client{}, err return models.Client{}, err
@ -21,6 +21,7 @@ func (db *Database) CreateClient(ctx db.TxContext, userid models.UserID, ctype m
TimestampCreated: time2DB(time.Now()), TimestampCreated: time2DB(time.Now()),
AgentModel: agentModel, AgentModel: agentModel,
AgentVersion: agentVersion, AgentVersion: agentVersion,
DescriptionName: descriptionName,
} }
_, err = sq.InsertSingle(ctx, tx, "clients", entity) _, err = sq.InsertSingle(ctx, tx, "clients", entity)
@ -164,3 +165,20 @@ func (db *Database) UpdateClientAgentVersion(ctx db.TxContext, clientid models.C
return nil return nil
} }
func (db *Database) UpdateClientDescriptionName(ctx db.TxContext, clientid models.ClientID, descriptionName *string) error {
tx, err := ctx.GetOrCreateTransaction(db)
if err != nil {
return err
}
_, err = tx.Exec(ctx, "UPDATE clients SET description_name = :vvv WHERE client_id = :cid", sq.PP{
"vvv": descriptionName,
"cid": clientid,
})
if err != nil {
return err
}
return nil
}

View File

@ -160,7 +160,7 @@ func (db *Database) Migrate(outerctx context.Context) error {
return err return err
} }
log.Info().Int("currschema", currschema).Msg("Upgrade schema from 3 -> 4 succesfuly") log.Info().Int("currschema", currschema).Msg("Upgrade schema from 3 -> 4 succesfully")
ppReInit = true ppReInit = true
} }
@ -185,6 +185,51 @@ func (db *Database) Migrate(outerctx context.Context) error {
} else { } else {
log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (primary db)") log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (primary db)")
} }
log.Info().Int("currschema", currschema).Msg("Upgrade schema from 4 -> 5")
_, err = tx.Exec(tctx, schema.PrimaryMigration_4_5, sq.PP{})
if err != nil {
return err
}
currschema = 5
err = db.WriteMetaInt(tctx, "schema", int64(currschema))
if err != nil {
return err
}
err = db.WriteMetaString(tctx, "schema_hash", schema.PrimarySchema[currschema].Hash)
if err != nil {
return err
}
log.Info().Int("currschema", currschema).Msg("Upgrade schema from 4 -> 5 succesfully")
ppReInit = true
}
if currschema == 5 {
schemaHashMeta, err := db.ReadMetaString(tctx, "schema_hash")
if err != nil {
return err
}
schemHashDB, err := sq.HashSqliteDatabase(tctx, tx)
if err != nil {
return err
}
if schemHashDB != langext.Coalesce(schemaHashMeta, "") || langext.Coalesce(schemaHashMeta, "") != schema.PrimarySchema[currschema].Hash {
log.Debug().Str("schemHashDB", schemHashDB).Msg("Schema (primary db)")
log.Debug().Str("schemaHashMeta", langext.Coalesce(schemaHashMeta, "")).Msg("Schema (primary db)")
log.Debug().Str("schemaHashAsset", schema.PrimarySchema[currschema].Hash).Msg("Schema (primary db)")
return errors.New("database schema does not match (primary db)")
} else {
log.Debug().Str("schemHash", schemHashDB).Msg("Verified Schema consistency (primary db)")
}
} }
if currschema != schema.PrimarySchemaVersion { if currschema != schema.PrimarySchemaVersion {

View File

@ -19,9 +19,15 @@ var primarySchema3 string
//go:embed primary_4.ddl //go:embed primary_4.ddl
var primarySchema4 string var primarySchema4 string
//go:embed primary_5.ddl
var primarySchema5 string
//go:embed primary_migration_3_4.ddl //go:embed primary_migration_3_4.ddl
var PrimaryMigration_3_4 string var PrimaryMigration_3_4 string
//go:embed primary_migration_4_5.ddl
var PrimaryMigration_4_5 string
//go:embed requests_1.ddl //go:embed requests_1.ddl
var requestsSchema1 string var requestsSchema1 string
@ -34,9 +40,10 @@ var PrimarySchema = map[int]Def{
2: {primarySchema2, "07ed1449114416ed043084a30e0722a5f97bf172161338d2f7106a8dfd387d0a"}, 2: {primarySchema2, "07ed1449114416ed043084a30e0722a5f97bf172161338d2f7106a8dfd387d0a"},
3: {primarySchema3, "65c2125ad0e12d02490cf2275f0067ef3c62a8522edf9a35ee8aa3f3c09b12e8"}, 3: {primarySchema3, "65c2125ad0e12d02490cf2275f0067ef3c62a8522edf9a35ee8aa3f3c09b12e8"},
4: {primarySchema4, "cb022156ab0e7aea39dd0c985428c43cae7d60e41ca8e9e5a84c774b3019d2ca"}, 4: {primarySchema4, "cb022156ab0e7aea39dd0c985428c43cae7d60e41ca8e9e5a84c774b3019d2ca"},
5: {primarySchema5, "04bd0d4a81540f69f10c8f8cd656a1fdf852d4ef7a2ab2918ca6369b5423b1b6"},
} }
var PrimarySchemaVersion = 4 var PrimarySchemaVersion = 5
var RequestsSchema = map[int]Def{ var RequestsSchema = map[int]Def{
0: {"", ""}, 0: {"", ""},

View File

@ -0,0 +1,234 @@
CREATE TABLE users
(
user_id TEXT NOT NULL,
username TEXT NULL DEFAULT NULL,
timestamp_created INTEGER NOT NULL,
timestamp_lastread INTEGER NULL DEFAULT NULL,
timestamp_lastsent INTEGER NULL DEFAULT NULL,
messages_sent INTEGER NOT NULL DEFAULT '0',
quota_used INTEGER NOT NULL DEFAULT '0',
quota_used_day TEXT NULL DEFAULT NULL,
is_pro INTEGER CHECK(is_pro IN (0, 1)) NOT NULL DEFAULT 0,
pro_token TEXT NULL DEFAULT NULL,
PRIMARY KEY (user_id)
) STRICT;
CREATE UNIQUE INDEX "idx_users_protoken" ON users (pro_token) WHERE pro_token IS NOT NULL;
CREATE TABLE keytokens
(
keytoken_id TEXT NOT NULL,
timestamp_created INTEGER NOT NULL,
timestamp_lastused INTEGER NULL DEFAULT NULL,
name TEXT NOT NULL,
owner_user_id TEXT NOT NULL,
all_channels INTEGER CHECK(all_channels IN (0, 1)) NOT NULL,
channels TEXT NOT NULL,
token TEXT NOT NULL,
permissions TEXT NOT NULL,
messages_sent INTEGER NOT NULL DEFAULT '0',
PRIMARY KEY (keytoken_id)
) STRICT;
CREATE UNIQUE INDEX "idx_keytokens_token" ON keytokens (token);
CREATE TABLE clients
(
client_id TEXT NOT NULL,
user_id TEXT NOT NULL,
type TEXT CHECK(type IN ('ANDROID', 'IOS')) NOT NULL,
fcm_token TEXT NOT NULL,
description_name TEXT NULL,
timestamp_created INTEGER NOT NULL,
agent_model TEXT NOT NULL,
agent_version TEXT NOT NULL,
PRIMARY KEY (client_id)
) STRICT;
CREATE INDEX "idx_clients_userid" ON clients (user_id);
CREATE UNIQUE INDEX "idx_clients_fcmtoken" ON clients (fcm_token);
CREATE TABLE channels
(
channel_id TEXT NOT NULL,
owner_user_id TEXT NOT NULL,
internal_name TEXT NOT NULL,
display_name TEXT NOT NULL,
description_name TEXT NULL,
subscribe_key TEXT NOT NULL,
timestamp_created INTEGER NOT NULL,
timestamp_lastsent INTEGER NULL DEFAULT NULL,
messages_sent INTEGER NOT NULL DEFAULT '0',
PRIMARY KEY (channel_id)
) STRICT;
CREATE UNIQUE INDEX "idx_channels_identity" ON channels (owner_user_id, internal_name);
CREATE TABLE subscriptions
(
subscription_id TEXT NOT NULL,
subscriber_user_id TEXT NOT NULL,
channel_owner_user_id TEXT NOT NULL,
channel_internal_name TEXT NOT NULL,
channel_id TEXT NOT NULL,
timestamp_created INTEGER NOT NULL,
confirmed INTEGER CHECK(confirmed IN (0, 1)) NOT NULL,
PRIMARY KEY (subscription_id)
) STRICT;
CREATE UNIQUE INDEX "idx_subscriptions_ref" ON subscriptions (subscriber_user_id, channel_owner_user_id, channel_internal_name);
CREATE INDEX "idx_subscriptions_chan" ON subscriptions (channel_id);
CREATE INDEX "idx_subscriptions_subuser" ON subscriptions (subscriber_user_id);
CREATE INDEX "idx_subscriptions_ownuser" ON subscriptions (channel_owner_user_id);
CREATE INDEX "idx_subscriptions_tsc" ON subscriptions (timestamp_created);
CREATE INDEX "idx_subscriptions_conf" ON subscriptions (confirmed);
CREATE TABLE messages
(
message_id TEXT NOT NULL,
sender_user_id TEXT NOT NULL,
channel_internal_name TEXT NOT NULL,
channel_id TEXT NOT NULL,
sender_ip TEXT NOT NULL,
sender_name TEXT NULL,
timestamp_real INTEGER NOT NULL,
timestamp_client INTEGER NULL,
title TEXT NOT NULL,
content TEXT NULL,
priority INTEGER CHECK(priority IN (0, 1, 2)) NOT NULL,
usr_message_id TEXT NULL,
used_key_id TEXT NOT NULL,
deleted INTEGER CHECK(deleted IN (0, 1)) NOT NULL DEFAULT '0',
PRIMARY KEY (message_id)
) STRICT;
CREATE INDEX "idx_messages_channel" ON messages (channel_internal_name COLLATE BINARY);
CREATE INDEX "idx_messages_channel_nc" ON messages (channel_internal_name COLLATE NOCASE);
CREATE UNIQUE INDEX "idx_messages_idempotency" ON messages (sender_user_id, usr_message_id COLLATE BINARY);
CREATE INDEX "idx_messages_senderip" ON messages (sender_ip COLLATE BINARY);
CREATE INDEX "idx_messages_sendername" ON messages (sender_name COLLATE BINARY);
CREATE INDEX "idx_messages_sendername_nc" ON messages (sender_name COLLATE NOCASE);
CREATE INDEX "idx_messages_title" ON messages (title COLLATE BINARY);
CREATE INDEX "idx_messages_title_nc" ON messages (title COLLATE NOCASE);
CREATE INDEX "idx_messages_usedkey" ON messages (sender_user_id, used_key_id);
CREATE INDEX "idx_messages_deleted" ON messages (deleted);
CREATE VIRTUAL TABLE messages_fts USING fts5
(
channel_internal_name,
sender_name,
title,
content,
tokenize = unicode61,
content = 'messages',
content_rowid = 'rowid'
);
CREATE TRIGGER fts_insert AFTER INSERT ON messages BEGIN
INSERT INTO messages_fts (rowid, channel_internal_name, sender_name, title, content) VALUES (new.rowid, new.channel_internal_name, new.sender_name, new.title, new.content);
END;
CREATE TRIGGER fts_update AFTER UPDATE ON messages BEGIN
INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.rowid, old.channel_internal_name, old.sender_name, old.title, old.content);
INSERT INTO messages_fts ( rowid, channel_internal_name, sender_name, title, content) VALUES ( new.rowid, new.channel_internal_name, new.sender_name, new.title, new.content);
END;
CREATE TRIGGER fts_delete AFTER DELETE ON messages BEGIN
INSERT INTO messages_fts (messages_fts, rowid, channel_internal_name, sender_name, title, content) VALUES ('delete', old.rowid, old.channel_internal_name, old.sender_name, old.title, old.content);
END;
CREATE TABLE deliveries
(
delivery_id TEXT NOT NULL,
message_id TEXT NOT NULL,
receiver_user_id TEXT NOT NULL,
receiver_client_id TEXT NOT NULL,
timestamp_created INTEGER NOT NULL,
timestamp_finalized INTEGER NULL,
status TEXT CHECK(status IN ('RETRY','SUCCESS','FAILED')) NOT NULL,
retry_count INTEGER NOT NULL DEFAULT 0,
next_delivery TEXT NULL DEFAULT NULL,
fcm_message_id TEXT NULL,
PRIMARY KEY (delivery_id)
) STRICT;
CREATE INDEX "idx_deliveries_receiver" ON deliveries (message_id, receiver_client_id);
CREATE TABLE compat_ids
(
old INTEGER NOT NULL,
new TEXT NOT NULL,
type TEXT NOT NULL
) STRICT;
CREATE UNIQUE INDEX "idx_compatids_new" ON compat_ids (new);
CREATE UNIQUE INDEX "idx_compatids_old" ON compat_ids (old, type);
CREATE TABLE compat_acks
(
user_id TEXT NOT NULL,
message_id TEXT NOT NULL
) STRICT;
CREATE INDEX "idx_compatacks_userid" ON compat_acks (user_id);
CREATE UNIQUE INDEX "idx_compatacks_messageid" ON compat_acks (message_id);
CREATE UNIQUE INDEX "idx_compatacks_userid_messageid" ON compat_acks (user_id, message_id);
CREATE TABLE compat_clients
(
client_id TEXT NOT NULL
) STRICT;
CREATE UNIQUE INDEX "idx_compatclient_clientid" ON compat_clients (client_id);
CREATE TABLE `meta`
(
meta_key TEXT NOT NULL,
value_int INTEGER NULL,
value_txt TEXT NULL,
value_real REAL NULL,
value_blob BLOB NULL,
PRIMARY KEY (meta_key)
) STRICT;
INSERT INTO meta (meta_key, value_int) VALUES ('schema', 3)

View File

@ -0,0 +1,6 @@
ALTER TABLE clients ADD COLUMN "description_name" TEXT NULL;

View File

@ -26,6 +26,7 @@ type Client struct {
TimestampCreated time.Time TimestampCreated time.Time
AgentModel string AgentModel string
AgentVersion string AgentVersion string
DescriptionName *string
} }
func (c Client) JSON() ClientJSON { func (c Client) JSON() ClientJSON {
@ -37,6 +38,7 @@ func (c Client) JSON() ClientJSON {
TimestampCreated: c.TimestampCreated.Format(time.RFC3339Nano), TimestampCreated: c.TimestampCreated.Format(time.RFC3339Nano),
AgentModel: c.AgentModel, AgentModel: c.AgentModel,
AgentVersion: c.AgentVersion, AgentVersion: c.AgentVersion,
DescriptionName: c.DescriptionName,
} }
} }
@ -48,6 +50,7 @@ type ClientJSON struct {
TimestampCreated string `json:"timestamp_created"` TimestampCreated string `json:"timestamp_created"`
AgentModel string `json:"agent_model"` AgentModel string `json:"agent_model"`
AgentVersion string `json:"agent_version"` AgentVersion string `json:"agent_version"`
DescriptionName *string `json:"description_name"`
} }
type ClientDB struct { type ClientDB struct {
@ -58,6 +61,7 @@ type ClientDB struct {
TimestampCreated int64 `db:"timestamp_created"` TimestampCreated int64 `db:"timestamp_created"`
AgentModel string `db:"agent_model"` AgentModel string `db:"agent_model"`
AgentVersion string `db:"agent_version"` AgentVersion string `db:"agent_version"`
DescriptionName *string `db:"description_name"`
} }
func (c ClientDB) Model() Client { func (c ClientDB) Model() Client {
@ -69,6 +73,7 @@ func (c ClientDB) Model() Client {
TimestampCreated: timeFromMilli(c.TimestampCreated), TimestampCreated: timeFromMilli(c.TimestampCreated),
AgentModel: c.AgentModel, AgentModel: c.AgentModel,
AgentVersion: c.AgentVersion, AgentVersion: c.AgentVersion,
DescriptionName: c.DescriptionName,
} }
} }

View File

@ -5,7 +5,7 @@ package models
import "gogs.mikescher.com/BlackForestBytes/goext/langext" import "gogs.mikescher.com/BlackForestBytes/goext/langext"
import "gogs.mikescher.com/BlackForestBytes/goext/enums" import "gogs.mikescher.com/BlackForestBytes/goext/enums"
const ChecksumEnumGenerator = "20ad4720103f17781ed6aaa102b503abb182ac08c077b42d4f56557ef38f28eb" // GoExtVersion: 0.0.463 const ChecksumEnumGenerator = "814434d6d179eec7fb90b380db859daf6051b5d767d83a20633f9ef78d66e857" // GoExtVersion: 0.0.463
// ================================ ClientType ================================ // ================================ ClientType ================================
// //

View File

@ -15,7 +15,7 @@ import "reflect"
import "regexp" import "regexp"
import "strings" import "strings"
const ChecksumCharsetIDGenerator = "20ad4720103f17781ed6aaa102b503abb182ac08c077b42d4f56557ef38f28eb" // GoExtVersion: 0.0.463 const ChecksumCharsetIDGenerator = "814434d6d179eec7fb90b380db859daf6051b5d767d83a20633f9ef78d66e857" // GoExtVersion: 0.0.463
const idlen = 24 const idlen = 24

View File

@ -19,37 +19,61 @@
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
"example": "test",
"name": "channel",
"in": "query"
},
{
"type": "string",
"example": "This is a message",
"name": "content", "name": "content",
"in": "query" "in": "query"
}, },
{ {
"type": "string", "type": "string",
"example": "P3TNH8mvv14fm",
"name": "key",
"in": "query"
},
{
"type": "string",
"example": "db8b0e6a-a08c-4646",
"name": "msg_id", "name": "msg_id",
"in": "query" "in": "query"
}, },
{ {
"enum": [
0,
1,
2
],
"type": "integer", "type": "integer",
"example": 1,
"name": "priority", "name": "priority",
"in": "query" "in": "query"
}, },
{
"type": "string",
"example": "example-server",
"name": "sender_name",
"in": "query"
},
{ {
"type": "number", "type": "number",
"example": 1669824037,
"name": "timestamp", "name": "timestamp",
"in": "query" "in": "query"
}, },
{ {
"type": "string", "type": "string",
"example": "Hello World",
"name": "title", "name": "title",
"in": "query" "in": "query"
}, },
{
"type": "integer",
"name": "user_id",
"in": "query"
},
{ {
"type": "string", "type": "string",
"name": "user_key", "example": "7725",
"name": "user_id",
"in": "query" "in": "query"
}, },
{ {
@ -62,37 +86,61 @@
}, },
{ {
"type": "string", "type": "string",
"example": "test",
"name": "channel",
"in": "formData"
},
{
"type": "string",
"example": "This is a message",
"name": "content", "name": "content",
"in": "formData" "in": "formData"
}, },
{ {
"type": "string", "type": "string",
"example": "P3TNH8mvv14fm",
"name": "key",
"in": "formData"
},
{
"type": "string",
"example": "db8b0e6a-a08c-4646",
"name": "msg_id", "name": "msg_id",
"in": "formData" "in": "formData"
}, },
{ {
"enum": [
0,
1,
2
],
"type": "integer", "type": "integer",
"example": 1,
"name": "priority", "name": "priority",
"in": "formData" "in": "formData"
}, },
{
"type": "string",
"example": "example-server",
"name": "sender_name",
"in": "formData"
},
{ {
"type": "number", "type": "number",
"example": 1669824037,
"name": "timestamp", "name": "timestamp",
"in": "formData" "in": "formData"
}, },
{ {
"type": "string", "type": "string",
"example": "Hello World",
"name": "title", "name": "title",
"in": "formData" "in": "formData"
}, },
{
"type": "integer",
"name": "user_id",
"in": "formData"
},
{ {
"type": "string", "type": "string",
"name": "user_key", "example": "7725",
"name": "user_id",
"in": "formData" "in": "formData"
} }
], ],
@ -2509,37 +2557,61 @@
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
"example": "test",
"name": "channel",
"in": "query"
},
{
"type": "string",
"example": "This is a message",
"name": "content", "name": "content",
"in": "query" "in": "query"
}, },
{ {
"type": "string", "type": "string",
"example": "P3TNH8mvv14fm",
"name": "key",
"in": "query"
},
{
"type": "string",
"example": "db8b0e6a-a08c-4646",
"name": "msg_id", "name": "msg_id",
"in": "query" "in": "query"
}, },
{ {
"enum": [
0,
1,
2
],
"type": "integer", "type": "integer",
"example": 1,
"name": "priority", "name": "priority",
"in": "query" "in": "query"
}, },
{
"type": "string",
"example": "example-server",
"name": "sender_name",
"in": "query"
},
{ {
"type": "number", "type": "number",
"example": 1669824037,
"name": "timestamp", "name": "timestamp",
"in": "query" "in": "query"
}, },
{ {
"type": "string", "type": "string",
"example": "Hello World",
"name": "title", "name": "title",
"in": "query" "in": "query"
}, },
{
"type": "integer",
"name": "user_id",
"in": "query"
},
{ {
"type": "string", "type": "string",
"name": "user_key", "example": "7725",
"name": "user_id",
"in": "query" "in": "query"
}, },
{ {
@ -2552,37 +2624,61 @@
}, },
{ {
"type": "string", "type": "string",
"example": "test",
"name": "channel",
"in": "formData"
},
{
"type": "string",
"example": "This is a message",
"name": "content", "name": "content",
"in": "formData" "in": "formData"
}, },
{ {
"type": "string", "type": "string",
"example": "P3TNH8mvv14fm",
"name": "key",
"in": "formData"
},
{
"type": "string",
"example": "db8b0e6a-a08c-4646",
"name": "msg_id", "name": "msg_id",
"in": "formData" "in": "formData"
}, },
{ {
"enum": [
0,
1,
2
],
"type": "integer", "type": "integer",
"example": 1,
"name": "priority", "name": "priority",
"in": "formData" "in": "formData"
}, },
{
"type": "string",
"example": "example-server",
"name": "sender_name",
"in": "formData"
},
{ {
"type": "number", "type": "number",
"example": 1669824037,
"name": "timestamp", "name": "timestamp",
"in": "formData" "in": "formData"
}, },
{ {
"type": "string", "type": "string",
"example": "Hello World",
"name": "title", "name": "title",
"in": "formData" "in": "formData"
}, },
{
"type": "integer",
"name": "user_id",
"in": "formData"
},
{ {
"type": "string", "type": "string",
"name": "user_key", "example": "7725",
"name": "user_id",
"in": "formData" "in": "formData"
} }
], ],
@ -2631,72 +2727,120 @@
"parameters": [ "parameters": [
{ {
"type": "string", "type": "string",
"example": "test",
"name": "channel",
"in": "query"
},
{
"type": "string",
"example": "This is a message",
"name": "content", "name": "content",
"in": "query" "in": "query"
}, },
{ {
"type": "string", "type": "string",
"example": "P3TNH8mvv14fm",
"name": "key",
"in": "query"
},
{
"type": "string",
"example": "db8b0e6a-a08c-4646",
"name": "msg_id", "name": "msg_id",
"in": "query" "in": "query"
}, },
{ {
"enum": [
0,
1,
2
],
"type": "integer", "type": "integer",
"example": 1,
"name": "priority", "name": "priority",
"in": "query" "in": "query"
}, },
{
"type": "string",
"example": "example-server",
"name": "sender_name",
"in": "query"
},
{ {
"type": "number", "type": "number",
"example": 1669824037,
"name": "timestamp", "name": "timestamp",
"in": "query" "in": "query"
}, },
{ {
"type": "string", "type": "string",
"example": "Hello World",
"name": "title", "name": "title",
"in": "query" "in": "query"
}, },
{ {
"type": "integer", "type": "string",
"example": "7725",
"name": "user_id", "name": "user_id",
"in": "query" "in": "query"
}, },
{ {
"type": "string", "type": "string",
"name": "user_key", "example": "test",
"in": "query" "name": "channel",
"in": "formData"
}, },
{ {
"type": "string", "type": "string",
"example": "This is a message",
"name": "content", "name": "content",
"in": "formData" "in": "formData"
}, },
{ {
"type": "string", "type": "string",
"example": "P3TNH8mvv14fm",
"name": "key",
"in": "formData"
},
{
"type": "string",
"example": "db8b0e6a-a08c-4646",
"name": "msg_id", "name": "msg_id",
"in": "formData" "in": "formData"
}, },
{ {
"enum": [
0,
1,
2
],
"type": "integer", "type": "integer",
"example": 1,
"name": "priority", "name": "priority",
"in": "formData" "in": "formData"
}, },
{
"type": "string",
"example": "example-server",
"name": "sender_name",
"in": "formData"
},
{ {
"type": "number", "type": "number",
"example": 1669824037,
"name": "timestamp", "name": "timestamp",
"in": "formData" "in": "formData"
}, },
{ {
"type": "string", "type": "string",
"example": "Hello World",
"name": "title", "name": "title",
"in": "formData" "in": "formData"
}, },
{
"type": "integer",
"name": "user_id",
"in": "formData"
},
{ {
"type": "string", "type": "string",
"name": "user_key", "example": "7725",
"name": "user_id",
"in": "formData" "in": "formData"
} }
], ],
@ -2895,6 +3039,9 @@
"client_type": { "client_type": {
"$ref": "#/definitions/models.ClientType" "$ref": "#/definitions/models.ClientType"
}, },
"description_name": {
"type": "string"
},
"fcm_token": { "fcm_token": {
"type": "string" "type": "string"
} }
@ -2940,6 +3087,9 @@
"client_type": { "client_type": {
"$ref": "#/definitions/models.ClientType" "$ref": "#/definitions/models.ClientType"
}, },
"description_name": {
"type": "string"
},
"fcm_token": { "fcm_token": {
"type": "string" "type": "string"
}, },
@ -3187,26 +3337,46 @@
"handler.SendMessage.combined": { "handler.SendMessage.combined": {
"type": "object", "type": "object",
"properties": { "properties": {
"channel": {
"type": "string",
"example": "test"
},
"content": { "content": {
"type": "string" "type": "string",
"example": "This is a message"
},
"key": {
"type": "string",
"example": "P3TNH8mvv14fm"
}, },
"msg_id": { "msg_id": {
"type": "string" "type": "string",
"example": "db8b0e6a-a08c-4646"
}, },
"priority": { "priority": {
"type": "integer" "type": "integer",
"enum": [
0,
1,
2
],
"example": 1
},
"sender_name": {
"type": "string",
"example": "example-server"
}, },
"timestamp": { "timestamp": {
"type": "number" "type": "number",
"example": 1669824037
}, },
"title": { "title": {
"type": "string" "type": "string",
"example": "Hello World"
}, },
"user_id": { "user_id": {
"type": "integer" "type": "string",
}, "example": "7725"
"user_key": {
"type": "string"
} }
} }
}, },
@ -3235,7 +3405,7 @@
"type": "integer" "type": "integer"
}, },
"scn_msg_id": { "scn_msg_id": {
"type": "integer" "type": "string"
}, },
"success": { "success": {
"type": "boolean" "type": "boolean"
@ -3471,6 +3641,9 @@
"client_id": { "client_id": {
"type": "string" "type": "string"
}, },
"description_name": {
"type": "string"
},
"fcm_token": { "fcm_token": {
"type": "string" "type": "string"
}, },

View File

@ -129,6 +129,8 @@ definitions:
type: string type: string
client_type: client_type:
$ref: '#/definitions/models.ClientType' $ref: '#/definitions/models.ClientType'
description_name:
type: string
fcm_token: fcm_token:
type: string type: string
required: required:
@ -163,6 +165,8 @@ definitions:
type: string type: string
client_type: client_type:
$ref: '#/definitions/models.ClientType' $ref: '#/definitions/models.ClientType'
description_name:
type: string
fcm_token: fcm_token:
type: string type: string
no_client: no_client:
@ -323,19 +327,36 @@ definitions:
type: object type: object
handler.SendMessage.combined: handler.SendMessage.combined:
properties: properties:
channel:
example: test
type: string
content: content:
example: This is a message
type: string
key:
example: P3TNH8mvv14fm
type: string type: string
msg_id: msg_id:
example: db8b0e6a-a08c-4646
type: string type: string
priority: priority:
enum:
- 0
- 1
- 2
example: 1
type: integer type: integer
sender_name:
example: example-server
type: string
timestamp: timestamp:
example: 1669824037
type: number type: number
title: title:
example: Hello World
type: string type: string
user_id: user_id:
type: integer example: "7725"
user_key:
type: string type: string
type: object type: object
handler.SendMessage.response: handler.SendMessage.response:
@ -355,7 +376,7 @@ definitions:
quota_max: quota_max:
type: integer type: integer
scn_msg_id: scn_msg_id:
type: integer type: string
success: success:
type: boolean type: boolean
suppress_send: suppress_send:
@ -508,6 +529,8 @@ definitions:
type: string type: string
client_id: client_id:
type: string type: string
description_name:
type: string
fcm_token: fcm_token:
type: string type: string
timestamp_created: timestamp_created:
@ -717,52 +740,90 @@ paths:
description: All parameter can be set via query-parameter or the json body. description: All parameter can be set via query-parameter or the json body.
Only UserID, UserKey and Title are required Only UserID, UserKey and Title are required
parameters: parameters:
- in: query - example: test
in: query
name: channel
type: string
- example: This is a message
in: query
name: content name: content
type: string type: string
- in: query - example: P3TNH8mvv14fm
in: query
name: key
type: string
- example: db8b0e6a-a08c-4646
in: query
name: msg_id name: msg_id
type: string type: string
- in: query - enum:
- 0
- 1
- 2
example: 1
in: query
name: priority name: priority
type: integer type: integer
- in: query - example: example-server
in: query
name: sender_name
type: string
- example: 1669824037
in: query
name: timestamp name: timestamp
type: number type: number
- in: query - example: Hello World
in: query
name: title name: title
type: string type: string
- in: query - example: "7725"
in: query
name: user_id name: user_id
type: integer
- in: query
name: user_key
type: string type: string
- description: ' ' - description: ' '
in: body in: body
name: post_body name: post_body
schema: schema:
$ref: '#/definitions/handler.SendMessage.combined' $ref: '#/definitions/handler.SendMessage.combined'
- in: formData - example: test
in: formData
name: channel
type: string
- example: This is a message
in: formData
name: content name: content
type: string type: string
- in: formData - example: P3TNH8mvv14fm
in: formData
name: key
type: string
- example: db8b0e6a-a08c-4646
in: formData
name: msg_id name: msg_id
type: string type: string
- in: formData - enum:
- 0
- 1
- 2
example: 1
in: formData
name: priority name: priority
type: integer type: integer
- in: formData - example: example-server
in: formData
name: sender_name
type: string
- example: 1669824037
in: formData
name: timestamp name: timestamp
type: number type: number
- in: formData - example: Hello World
in: formData
name: title name: title
type: string type: string
- in: formData - example: "7725"
in: formData
name: user_id name: user_id
type: integer
- in: formData
name: user_key
type: string type: string
responses: responses:
"200": "200":
@ -2420,52 +2481,90 @@ paths:
description: All parameter can be set via query-parameter or the json body. description: All parameter can be set via query-parameter or the json body.
Only UserID, UserKey and Title are required Only UserID, UserKey and Title are required
parameters: parameters:
- in: query - example: test
in: query
name: channel
type: string
- example: This is a message
in: query
name: content name: content
type: string type: string
- in: query - example: P3TNH8mvv14fm
in: query
name: key
type: string
- example: db8b0e6a-a08c-4646
in: query
name: msg_id name: msg_id
type: string type: string
- in: query - enum:
- 0
- 1
- 2
example: 1
in: query
name: priority name: priority
type: integer type: integer
- in: query - example: example-server
in: query
name: sender_name
type: string
- example: 1669824037
in: query
name: timestamp name: timestamp
type: number type: number
- in: query - example: Hello World
in: query
name: title name: title
type: string type: string
- in: query - example: "7725"
in: query
name: user_id name: user_id
type: integer
- in: query
name: user_key
type: string type: string
- description: ' ' - description: ' '
in: body in: body
name: post_body name: post_body
schema: schema:
$ref: '#/definitions/handler.SendMessage.combined' $ref: '#/definitions/handler.SendMessage.combined'
- in: formData - example: test
in: formData
name: channel
type: string
- example: This is a message
in: formData
name: content name: content
type: string type: string
- in: formData - example: P3TNH8mvv14fm
in: formData
name: key
type: string
- example: db8b0e6a-a08c-4646
in: formData
name: msg_id name: msg_id
type: string type: string
- in: formData - enum:
- 0
- 1
- 2
example: 1
in: formData
name: priority name: priority
type: integer type: integer
- in: formData - example: example-server
in: formData
name: sender_name
type: string
- example: 1669824037
in: formData
name: timestamp name: timestamp
type: number type: number
- in: formData - example: Hello World
in: formData
name: title name: title
type: string type: string
- in: formData - example: "7725"
in: formData
name: user_id name: user_id
type: integer
- in: formData
name: user_key
type: string type: string
responses: responses:
"200": "200":
@ -2498,47 +2597,85 @@ paths:
description: All parameter can be set via query-parameter or form-data body. description: All parameter can be set via query-parameter or form-data body.
Only UserID, UserKey and Title are required Only UserID, UserKey and Title are required
parameters: parameters:
- in: query - example: test
in: query
name: channel
type: string
- example: This is a message
in: query
name: content name: content
type: string type: string
- in: query - example: P3TNH8mvv14fm
in: query
name: key
type: string
- example: db8b0e6a-a08c-4646
in: query
name: msg_id name: msg_id
type: string type: string
- in: query - enum:
- 0
- 1
- 2
example: 1
in: query
name: priority name: priority
type: integer type: integer
- in: query - example: example-server
in: query
name: sender_name
type: string
- example: 1669824037
in: query
name: timestamp name: timestamp
type: number type: number
- in: query - example: Hello World
in: query
name: title name: title
type: string type: string
- in: query - example: "7725"
in: query
name: user_id name: user_id
type: integer
- in: query
name: user_key
type: string type: string
- in: formData - example: test
in: formData
name: channel
type: string
- example: This is a message
in: formData
name: content name: content
type: string type: string
- in: formData - example: P3TNH8mvv14fm
in: formData
name: key
type: string
- example: db8b0e6a-a08c-4646
in: formData
name: msg_id name: msg_id
type: string type: string
- in: formData - enum:
- 0
- 1
- 2
example: 1
in: formData
name: priority name: priority
type: integer type: integer
- in: formData - example: example-server
in: formData
name: sender_name
type: string
- example: 1669824037
in: formData
name: timestamp name: timestamp
type: number type: number
- in: formData - example: Hello World
in: formData
name: title name: title
type: string type: string
- in: formData - example: "7725"
in: formData
name: user_id name: user_id
type: integer
- in: formData
name: user_key
type: string type: string
responses: responses:
"200": "200":

View File

@ -357,10 +357,108 @@ func TestPrimaryDB_Migrate_from_3(t *testing.T) {
tt.TestFailIfErr(t, err) tt.TestFailIfErr(t, err)
} }
//================================================
{ {
err = db1.Migrate(ctx) err = db1.Migrate(ctx)
tt.TestFailIfErr(t, err) tt.TestFailIfErr(t, err)
} }
//================================================
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema2, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema2", schema.PrimarySchemaVersion, schema2)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, db1.DB())
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", schema.PrimarySchema[schema.PrimarySchemaVersion].Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = db1.Stop(ctx)
tt.TestFailIfErr(t, err)
}
}
func TestPrimaryDB_Migrate_from_4(t *testing.T) {
dbf1, dbf2, dbf3, conf, stop := tt.StartSimpleTestspace(t)
defer stop()
ctx := context.Background()
tt.AssertAny(dbf1)
tt.AssertAny(dbf2)
tt.AssertAny(dbf3)
tt.AssertAny(conf)
{
url := fmt.Sprintf("file:%s", dbf1)
xdb, err := sqlx.Open("sqlite3", url)
tt.TestFailIfErr(t, err)
qqdb := sq.NewDB(xdb, sq.DBOptions{})
schemavers := 4
dbschema := schema.PrimarySchema[schemavers]
_, err = qqdb.Exec(ctx, dbschema.SQL, sq.PP{})
tt.TestFailIfErr(t, err)
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_int) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_int = :val", sq.PP{
"key": "schema",
"val": schemavers,
})
_, err = qqdb.Exec(ctx, "INSERT INTO meta (meta_key, value_txt) VALUES (:key, :val) ON CONFLICT(meta_key) DO UPDATE SET value_txt = :val", sq.PP{
"key": "schema_hash",
"val": dbschema.Hash,
})
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schemHashDB, err := sq.HashSqliteDatabase(tctx, qqdb)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schemHashDB", dbschema.Hash, schemHashDB)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
err = qqdb.Exit()
tt.TestFailIfErr(t, err)
}
{
db1, err := primary.NewPrimaryDatabase(conf)
tt.TestFailIfErr(t, err)
{
tctx := simplectx.CreateSimpleContext(ctx, nil)
schema1, err := db1.ReadSchema(tctx)
tt.TestFailIfErr(t, err)
tt.AssertEqual(t, "schema1", 4, schema1)
err = tctx.CommitTransaction()
tt.TestFailIfErr(t, err)
}
//================================================
{
err = db1.Migrate(ctx)
tt.TestFailIfErr(t, err)
}
//================================================
{ {
tctx := simplectx.CreateSimpleContext(ctx, nil) tctx := simplectx.CreateSimpleContext(ctx, nil)