mirror of
https://github.com/hoernschen/dendrite.git
synced 2024-12-26 15:08:28 +00:00
Migrate clientapi storage to use migrate library
This commit is contained in:
parent
c2b4f01f6c
commit
73ae1bd58c
13 changed files with 113 additions and 146 deletions
6
db-schema/accounts/1511790253_initial.down.sql
Normal file
6
db-schema/accounts/1511790253_initial.down.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
DROP TABLE IF EXISTS account_data;
|
||||||
|
DROP TABLE IF EXISTS account_accounts;
|
||||||
|
DROP TABLE IF EXISTS account_filter;
|
||||||
|
DROP TABLE IF EXISTS account_memberships;
|
||||||
|
DROP TABLE IF EXISTS account_profiles;
|
||||||
|
DROP TABLE IF EXISTS account_threepid;
|
73
db-schema/accounts/1511790253_initial.up.sql
Normal file
73
db-schema/accounts/1511790253_initial.up.sql
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS account_data (
|
||||||
|
-- The Matrix user ID localpart for this account
|
||||||
|
localpart TEXT NOT NULL,
|
||||||
|
-- The room ID for this data (empty string if not specific to a room)
|
||||||
|
room_id TEXT,
|
||||||
|
-- The account data type
|
||||||
|
type TEXT NOT NULL,
|
||||||
|
-- The account data content
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(localpart, room_id, type)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS account_accounts (
|
||||||
|
-- The Matrix user ID localpart for this account
|
||||||
|
localpart TEXT NOT NULL PRIMARY KEY,
|
||||||
|
-- When this account was first created, as a unix timestamp (ms resolution).
|
||||||
|
created_ts BIGINT NOT NULL,
|
||||||
|
-- The password hash for this account. Can be NULL if this is a passwordless account.
|
||||||
|
password_hash TEXT
|
||||||
|
-- TODO:
|
||||||
|
-- is_guest, is_admin, appservice_id, upgraded_ts, devices, any email reset stuff?
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS account_filter (
|
||||||
|
-- The filter
|
||||||
|
filter TEXT NOT NULL,
|
||||||
|
-- The ID
|
||||||
|
id SERIAL UNIQUE,
|
||||||
|
-- The localpart of the Matrix user ID associated to this filter
|
||||||
|
localpart TEXT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(id, localpart)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS account_filter_localpart ON account_filter(localpart);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS account_memberships (
|
||||||
|
-- The Matrix user ID localpart for the member
|
||||||
|
localpart TEXT NOT NULL,
|
||||||
|
-- The room this user is a member of
|
||||||
|
room_id TEXT NOT NULL,
|
||||||
|
-- The ID of the join membership event
|
||||||
|
event_id TEXT NOT NULL,
|
||||||
|
|
||||||
|
-- A user can only be member of a room once
|
||||||
|
PRIMARY KEY (localpart, room_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Use index to process deletion by ID more efficiently
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS account_membership_event_id ON account_memberships(event_id);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS account_profiles (
|
||||||
|
-- The Matrix user ID localpart for this account
|
||||||
|
localpart TEXT NOT NULL PRIMARY KEY,
|
||||||
|
-- The display name for this account
|
||||||
|
display_name TEXT,
|
||||||
|
-- The URL of the avatar for this account
|
||||||
|
avatar_url TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS account_threepid (
|
||||||
|
-- The third party identifier
|
||||||
|
threepid TEXT NOT NULL,
|
||||||
|
-- The 3PID medium
|
||||||
|
medium TEXT NOT NULL DEFAULT 'email',
|
||||||
|
-- The localpart of the Matrix user ID associated to this 3PID
|
||||||
|
localpart TEXT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(threepid, medium)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS account_threepid_localpart ON account_threepid(localpart);
|
1
db-schema/devices/1511791787_initial.down.sql
Normal file
1
db-schema/devices/1511791787_initial.down.sql
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DROP TABLE IF EXISTS device_devices;
|
21
db-schema/devices/1511791787_initial.up.sql
Normal file
21
db-schema/devices/1511791787_initial.up.sql
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
-- Stores data about devices.
|
||||||
|
CREATE TABLE IF NOT EXISTS device_devices (
|
||||||
|
-- The access token granted to this device. This has to be the primary key
|
||||||
|
-- so we can distinguish which device is making a given request.
|
||||||
|
access_token TEXT NOT NULL PRIMARY KEY,
|
||||||
|
-- The device identifier. This only needs to uniquely identify a device for a given user, not globally.
|
||||||
|
-- access_tokens will be clobbered based on the device ID for a user.
|
||||||
|
device_id TEXT NOT NULL,
|
||||||
|
-- The Matrix user ID localpart for this device. This is preferable to storing the full user_id
|
||||||
|
-- as it is smaller, makes it clearer that we only manage devices for our own users, and may make
|
||||||
|
-- migration to different domain names easier.
|
||||||
|
localpart TEXT NOT NULL,
|
||||||
|
-- When this devices was first recognised on the network, as a unix timestamp (ms resolution).
|
||||||
|
created_ts BIGINT NOT NULL,
|
||||||
|
-- The display name, human friendlier than device_id and updatable
|
||||||
|
display_name TEXT
|
||||||
|
-- TODO: device keys, device display names, last used ts and IP address?, token restrictions (if 3rd-party OAuth app)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Device IDs must be unique for a given user.
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS device_localpart_id_idx ON device_devices(localpart, device_id);
|
|
@ -21,22 +21,6 @@ import (
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
const accountDataSchema = `
|
|
||||||
-- Stores data about accounts data.
|
|
||||||
CREATE TABLE IF NOT EXISTS account_data (
|
|
||||||
-- The Matrix user ID localpart for this account
|
|
||||||
localpart TEXT NOT NULL,
|
|
||||||
-- The room ID for this data (empty string if not specific to a room)
|
|
||||||
room_id TEXT,
|
|
||||||
-- The account data type
|
|
||||||
type TEXT NOT NULL,
|
|
||||||
-- The account data content
|
|
||||||
content TEXT NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY(localpart, room_id, type)
|
|
||||||
);
|
|
||||||
`
|
|
||||||
|
|
||||||
const insertAccountDataSQL = `
|
const insertAccountDataSQL = `
|
||||||
INSERT INTO account_data(localpart, room_id, type, content) VALUES($1, $2, $3, $4)
|
INSERT INTO account_data(localpart, room_id, type, content) VALUES($1, $2, $3, $4)
|
||||||
ON CONFLICT (localpart, room_id, type) DO UPDATE SET content = EXCLUDED.content
|
ON CONFLICT (localpart, room_id, type) DO UPDATE SET content = EXCLUDED.content
|
||||||
|
@ -55,10 +39,6 @@ type accountDataStatements struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *accountDataStatements) prepare(db *sql.DB) (err error) {
|
func (s *accountDataStatements) prepare(db *sql.DB) (err error) {
|
||||||
_, err = db.Exec(accountDataSchema)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.insertAccountDataStmt, err = db.Prepare(insertAccountDataSQL); err != nil {
|
if s.insertAccountDataStmt, err = db.Prepare(insertAccountDataSQL); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,20 +24,6 @@ import (
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
const accountsSchema = `
|
|
||||||
-- Stores data about accounts.
|
|
||||||
CREATE TABLE IF NOT EXISTS account_accounts (
|
|
||||||
-- The Matrix user ID localpart for this account
|
|
||||||
localpart TEXT NOT NULL PRIMARY KEY,
|
|
||||||
-- When this account was first created, as a unix timestamp (ms resolution).
|
|
||||||
created_ts BIGINT NOT NULL,
|
|
||||||
-- The password hash for this account. Can be NULL if this is a passwordless account.
|
|
||||||
password_hash TEXT
|
|
||||||
-- TODO:
|
|
||||||
-- is_guest, is_admin, appservice_id, upgraded_ts, devices, any email reset stuff?
|
|
||||||
);
|
|
||||||
`
|
|
||||||
|
|
||||||
const insertAccountSQL = "" +
|
const insertAccountSQL = "" +
|
||||||
"INSERT INTO account_accounts(localpart, created_ts, password_hash) VALUES ($1, $2, $3)"
|
"INSERT INTO account_accounts(localpart, created_ts, password_hash) VALUES ($1, $2, $3)"
|
||||||
|
|
||||||
|
@ -57,10 +43,6 @@ type accountsStatements struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *accountsStatements) prepare(db *sql.DB, server gomatrixserverlib.ServerName) (err error) {
|
func (s *accountsStatements) prepare(db *sql.DB, server gomatrixserverlib.ServerName) (err error) {
|
||||||
_, err = db.Exec(accountsSchema)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.insertAccountStmt, err = db.Prepare(insertAccountSQL); err != nil {
|
if s.insertAccountStmt, err = db.Prepare(insertAccountSQL); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,22 +21,6 @@ import (
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
const filterSchema = `
|
|
||||||
-- Stores data about filters
|
|
||||||
CREATE TABLE IF NOT EXISTS account_filter (
|
|
||||||
-- The filter
|
|
||||||
filter TEXT NOT NULL,
|
|
||||||
-- The ID
|
|
||||||
id SERIAL UNIQUE,
|
|
||||||
-- The localpart of the Matrix user ID associated to this filter
|
|
||||||
localpart TEXT NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY(id, localpart)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS account_filter_localpart ON account_filter(localpart);
|
|
||||||
`
|
|
||||||
|
|
||||||
const selectFilterSQL = "" +
|
const selectFilterSQL = "" +
|
||||||
"SELECT filter FROM account_filter WHERE localpart = $1 AND id = $2"
|
"SELECT filter FROM account_filter WHERE localpart = $1 AND id = $2"
|
||||||
|
|
||||||
|
@ -53,10 +37,6 @@ type filterStatements struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *filterStatements) prepare(db *sql.DB) (err error) {
|
func (s *filterStatements) prepare(db *sql.DB) (err error) {
|
||||||
_, err = db.Exec(filterSchema)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.selectFilterStmt, err = db.Prepare(selectFilterSQL); err != nil {
|
if s.selectFilterStmt, err = db.Prepare(selectFilterSQL); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,24 +22,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const membershipSchema = `
|
|
||||||
-- Stores data about users memberships to rooms.
|
|
||||||
CREATE TABLE IF NOT EXISTS account_memberships (
|
|
||||||
-- The Matrix user ID localpart for the member
|
|
||||||
localpart TEXT NOT NULL,
|
|
||||||
-- The room this user is a member of
|
|
||||||
room_id TEXT NOT NULL,
|
|
||||||
-- The ID of the join membership event
|
|
||||||
event_id TEXT NOT NULL,
|
|
||||||
|
|
||||||
-- A user can only be member of a room once
|
|
||||||
PRIMARY KEY (localpart, room_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Use index to process deletion by ID more efficiently
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS account_membership_event_id ON account_memberships(event_id);
|
|
||||||
`
|
|
||||||
|
|
||||||
const insertMembershipSQL = `
|
const insertMembershipSQL = `
|
||||||
INSERT INTO account_memberships(localpart, room_id, event_id) VALUES ($1, $2, $3)
|
INSERT INTO account_memberships(localpart, room_id, event_id) VALUES ($1, $2, $3)
|
||||||
ON CONFLICT (localpart, room_id) DO UPDATE SET event_id = EXCLUDED.event_id
|
ON CONFLICT (localpart, room_id) DO UPDATE SET event_id = EXCLUDED.event_id
|
||||||
|
@ -58,10 +40,6 @@ type membershipStatements struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *membershipStatements) prepare(db *sql.DB) (err error) {
|
func (s *membershipStatements) prepare(db *sql.DB) (err error) {
|
||||||
_, err = db.Exec(membershipSchema)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.deleteMembershipsByEventIDsStmt, err = db.Prepare(deleteMembershipsByEventIDsSQL); err != nil {
|
if s.deleteMembershipsByEventIDsStmt, err = db.Prepare(deleteMembershipsByEventIDsSQL); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,18 +21,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const profilesSchema = `
|
|
||||||
-- Stores data about accounts profiles.
|
|
||||||
CREATE TABLE IF NOT EXISTS account_profiles (
|
|
||||||
-- The Matrix user ID localpart for this account
|
|
||||||
localpart TEXT NOT NULL PRIMARY KEY,
|
|
||||||
-- The display name for this account
|
|
||||||
display_name TEXT,
|
|
||||||
-- The URL of the avatar for this account
|
|
||||||
avatar_url TEXT
|
|
||||||
);
|
|
||||||
`
|
|
||||||
|
|
||||||
const insertProfileSQL = "" +
|
const insertProfileSQL = "" +
|
||||||
"INSERT INTO account_profiles(localpart, display_name, avatar_url) VALUES ($1, $2, $3)"
|
"INSERT INTO account_profiles(localpart, display_name, avatar_url) VALUES ($1, $2, $3)"
|
||||||
|
|
||||||
|
@ -53,10 +41,6 @@ type profilesStatements struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *profilesStatements) prepare(db *sql.DB) (err error) {
|
func (s *profilesStatements) prepare(db *sql.DB) (err error) {
|
||||||
_, err = db.Exec(profilesSchema)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.insertProfileStmt, err = db.Prepare(insertProfileSQL); err != nil {
|
if s.insertProfileStmt, err = db.Prepare(insertProfileSQL); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/common"
|
"github.com/matrix-org/dendrite/common"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
// Import the postgres database driver.
|
|
||||||
_ "github.com/lib/pq"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Database represents an account database
|
// Database represents an account database
|
||||||
|
@ -47,6 +45,12 @@ func NewDatabase(dataSourceName string, serverName gomatrixserverlib.ServerName)
|
||||||
if db, err = sql.Open("postgres", dataSourceName); err != nil {
|
if db, err = sql.Open("postgres", dataSourceName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = common.DoMigrations(db, "accounts")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
partitions := common.PartitionOffsetStatements{}
|
partitions := common.PartitionOffsetStatements{}
|
||||||
if err = partitions.Prepare(db, "account"); err != nil {
|
if err = partitions.Prepare(db, "account"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -23,22 +23,6 @@ import (
|
||||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const threepidSchema = `
|
|
||||||
-- Stores data about third party identifiers
|
|
||||||
CREATE TABLE IF NOT EXISTS account_threepid (
|
|
||||||
-- The third party identifier
|
|
||||||
threepid TEXT NOT NULL,
|
|
||||||
-- The 3PID medium
|
|
||||||
medium TEXT NOT NULL DEFAULT 'email',
|
|
||||||
-- The localpart of the Matrix user ID associated to this 3PID
|
|
||||||
localpart TEXT NOT NULL,
|
|
||||||
|
|
||||||
PRIMARY KEY(threepid, medium)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS account_threepid_localpart ON account_threepid(localpart);
|
|
||||||
`
|
|
||||||
|
|
||||||
const selectLocalpartForThreePIDSQL = "" +
|
const selectLocalpartForThreePIDSQL = "" +
|
||||||
"SELECT localpart FROM account_threepid WHERE threepid = $1 AND medium = $2"
|
"SELECT localpart FROM account_threepid WHERE threepid = $1 AND medium = $2"
|
||||||
|
|
||||||
|
@ -59,10 +43,6 @@ type threepidStatements struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *threepidStatements) prepare(db *sql.DB) (err error) {
|
func (s *threepidStatements) prepare(db *sql.DB) (err error) {
|
||||||
_, err = db.Exec(threepidSchema)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.selectLocalpartForThreePIDStmt, err = db.Prepare(selectLocalpartForThreePIDSQL); err != nil {
|
if s.selectLocalpartForThreePIDStmt, err = db.Prepare(selectLocalpartForThreePIDSQL); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,30 +26,6 @@ import (
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
)
|
)
|
||||||
|
|
||||||
const devicesSchema = `
|
|
||||||
-- Stores data about devices.
|
|
||||||
CREATE TABLE IF NOT EXISTS device_devices (
|
|
||||||
-- The access token granted to this device. This has to be the primary key
|
|
||||||
-- so we can distinguish which device is making a given request.
|
|
||||||
access_token TEXT NOT NULL PRIMARY KEY,
|
|
||||||
-- The device identifier. This only needs to uniquely identify a device for a given user, not globally.
|
|
||||||
-- access_tokens will be clobbered based on the device ID for a user.
|
|
||||||
device_id TEXT NOT NULL,
|
|
||||||
-- The Matrix user ID localpart for this device. This is preferable to storing the full user_id
|
|
||||||
-- as it is smaller, makes it clearer that we only manage devices for our own users, and may make
|
|
||||||
-- migration to different domain names easier.
|
|
||||||
localpart TEXT NOT NULL,
|
|
||||||
-- When this devices was first recognised on the network, as a unix timestamp (ms resolution).
|
|
||||||
created_ts BIGINT NOT NULL,
|
|
||||||
-- The display name, human friendlier than device_id and updatable
|
|
||||||
display_name TEXT
|
|
||||||
-- TODO: device keys, device display names, last used ts and IP address?, token restrictions (if 3rd-party OAuth app)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Device IDs must be unique for a given user.
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS device_localpart_id_idx ON device_devices(localpart, device_id);
|
|
||||||
`
|
|
||||||
|
|
||||||
const insertDeviceSQL = "" +
|
const insertDeviceSQL = "" +
|
||||||
"INSERT INTO device_devices(device_id, localpart, access_token, created_ts, display_name) VALUES ($1, $2, $3, $4, $5)"
|
"INSERT INTO device_devices(device_id, localpart, access_token, created_ts, display_name) VALUES ($1, $2, $3, $4, $5)"
|
||||||
|
|
||||||
|
@ -83,10 +59,6 @@ type devicesStatements struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *devicesStatements) prepare(db *sql.DB, server gomatrixserverlib.ServerName) (err error) {
|
func (s *devicesStatements) prepare(db *sql.DB, server gomatrixserverlib.ServerName) (err error) {
|
||||||
_, err = db.Exec(devicesSchema)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s.insertDeviceStmt, err = db.Prepare(insertDeviceSQL); err != nil {
|
if s.insertDeviceStmt, err = db.Prepare(insertDeviceSQL); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,12 @@ func NewDatabase(dataSourceName string, serverName gomatrixserverlib.ServerName)
|
||||||
if db, err = sql.Open("postgres", dataSourceName); err != nil {
|
if db, err = sql.Open("postgres", dataSourceName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = common.DoMigrations(db, "devices")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
d := devicesStatements{}
|
d := devicesStatements{}
|
||||||
if err = d.prepare(db, serverName); err != nil {
|
if err = d.prepare(db, serverName); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
Loading…
Reference in a new issue