add a test that checks collation on new migrations (#29309)

> closes #26403

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [x] If database migrations are included, checked table schema to
confirm autoupdate
- For database migrations:
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
- [x] Added/updated automated tests
- [x] Manual QA for all new/changed functionality
This commit is contained in:
Jahziel Villasana-Espinoza 2025-05-29 17:00:30 -04:00 committed by GitHub
parent dfb0b53a24
commit 9d2b07f76f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
65 changed files with 271 additions and 154 deletions

View file

@ -72,20 +72,6 @@ jobs:
index=$((index+1))
done
- name: Prevent hosts foreign keys
run: |
# grep exits with an error code if it doesn't find a match, so this condition
# is only true if it a) finds a matching migrations file in the diff, and b)
# finds an FK to hosts in one of the migrations files.
#
# grep prints the matches, which will help figure out where those references are.
if git diff --name-only origin/main | grep "migrations/" | xargs grep -i -E 'references\s*hosts\s*\(\s*id\s*\)' ; then
echo "❌ fail: hosts foreign keys are not allowed"
echo "Ref: https://github.com/fleetdm/fleet/blob/main/handbook/engineering/scaling-fleet.md#foreign-keys-and-locking"
exit 1
fi
- name: Install Go
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
with:
@ -112,3 +98,16 @@ jobs:
echo "please run 'make test-schema' and commit the changes"
exit 1
fi
- name: Prevent hosts foreign keys
run: |
# grep exits with an error code if it doesn't find a match, so this condition
# is only true if it a) finds a matching migrations file in the diff, and b)
# finds an FK to hosts in one of the migrations files.
#
# grep prints the matches, which will help figure out where those references are.
if git diff --name-only origin/main | grep "migrations/" | xargs grep -i -E 'references\s*hosts\s*\(\s*id\s*\)' ; then
echo "❌ fail: hosts foreign keys are not allowed"
echo "Ref: https://github.com/fleetdm/fleet/blob/main/handbook/engineering/scaling-fleet.md#foreign-keys-and-locking"
exit 1
fi

View file

@ -0,0 +1,2 @@
- Updated migrations to use the `utf8mb4_unicode_ci` collation across all tables and added a test to
validate that new migrations use this collation.

View file

@ -12,39 +12,39 @@ func Up_20161118212528(tx *sql.Tx) error {
_, err := tx.Exec(
"CREATE TABLE `hosts` (" +
"`id` int(10) unsigned NOT NULL AUTO_INCREMENT," +
"`osquery_host_id` varchar(255) NOT NULL," +
"`osquery_host_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL," +
"`created_at` timestamp DEFAULT CURRENT_TIMESTAMP," +
"`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP," +
"`deleted_at` timestamp NULL DEFAULT NULL," +
"`deleted` tinyint(1) NOT NULL DEFAULT FALSE," +
"`detail_update_time` timestamp NULL DEFAULT NULL," +
"`node_key` varchar(255) DEFAULT NULL," +
"`host_name` varchar(255) NOT NULL DEFAULT ''," +
"`uuid` varchar(255) NOT NULL DEFAULT ''," +
"`platform` varchar(255) NOT NULL DEFAULT ''," +
"`osquery_version` varchar(255) NOT NULL DEFAULT ''," +
"`os_version` varchar(255) NOT NULL DEFAULT ''," +
"`build` varchar(255) NOT NULL DEFAULT ''," +
"`platform_like` varchar(255) NOT NULL DEFAULT ''," +
"`code_name` varchar(255) NOT NULL DEFAULT ''," +
"`node_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL," +
"`host_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`uuid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`platform` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`osquery_version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`os_version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`build` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`platform_like` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`code_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`uptime` bigint(20) NOT NULL DEFAULT 0," +
"`physical_memory` bigint(20) NOT NULL DEFAULT 0," +
"`cpu_type` varchar(255) NOT NULL DEFAULT ''," +
"`cpu_subtype` varchar(255) NOT NULL DEFAULT ''," +
"`cpu_brand` varchar(255) NOT NULL DEFAULT ''," +
"`cpu_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`cpu_subtype` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`cpu_brand` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`cpu_physical_cores` int NOT NULL DEFAULT 0," +
"`cpu_logical_cores` int NOT NULL DEFAULT 0," +
"`hardware_vendor` varchar(255) NOT NULL DEFAULT ''," +
"`hardware_model` varchar(255) NOT NULL DEFAULT ''," +
"`hardware_version` varchar(255) NOT NULL DEFAULT ''," +
"`hardware_serial` varchar(255) NOT NULL DEFAULT ''," +
"`computer_name` varchar(255) NOT NULL DEFAULT ''," +
"`hardware_vendor` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`hardware_model` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`hardware_version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`hardware_serial` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`computer_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"`primary_ip_id` INT(10) UNSIGNED DEFAULT NULL, " +
"PRIMARY KEY (`id`)," +
"UNIQUE KEY `idx_host_unique_nodekey` (`node_key`)," +
"UNIQUE KEY `idx_osquery_host_id` (`osquery_host_id`)," +
"FULLTEXT KEY `hosts_search` (`host_name`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -25,7 +25,7 @@ func Up_20161118212538(tx *sql.Tx) error {
"PRIMARY KEY (`id`)," +
"UNIQUE KEY `idx_invite_unique_email` (`email`)," +
"UNIQUE KEY `idx_invite_unique_key` (`token`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -27,7 +27,7 @@ func Up_20161118212557(tx *sql.Tx) error {
"PRIMARY KEY (`id`)," +
"UNIQUE KEY `idx_label_unique_name` (`name`)," +
"FULLTEXT KEY `labels_search` (`name`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -25,7 +25,7 @@ func Up_20161118212613(tx *sql.Tx) error {
"`version` varchar(255) DEFAULT NULL," +
"`shard` int(10) unsigned DEFAULT NULL," +
"PRIMARY KEY (`id`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -23,7 +23,7 @@ func Up_20161118212630(tx *sql.Tx) error {
"`created_by` int(10) unsigned DEFAULT NULL," +
"PRIMARY KEY (`id`)," +
"UNIQUE KEY `idx_pack_unique_name` (`name`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -18,7 +18,7 @@ func Up_20161118212641(tx *sql.Tx) error {
"`user_id` int(10) unsigned NOT NULL," +
"`token` varchar(1024) NOT NULL," +
"PRIMARY KEY (`id`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -29,7 +29,7 @@ func Up_20161118212649(tx *sql.Tx) error {
"PRIMARY KEY (`id`)," +
"UNIQUE KEY `idx_user_unique_username` (`username`)," +
"UNIQUE KEY `idx_user_unique_email` (`email`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -18,7 +18,7 @@ func Up_20161118212656(tx *sql.Tx) error {
"`key` varchar(255) NOT NULL," +
"PRIMARY KEY (`id`)," +
"UNIQUE KEY `idx_session_unique_key` (`key`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -23,7 +23,7 @@ func Up_20161118212758(tx *sql.Tx) error {
"`author_id` int(10) unsigned DEFAULT NULL," +
"PRIMARY KEY (`id`)," +
"FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON DELETE SET NULL" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -35,7 +35,7 @@ func Up_20161128234849(tx *sql.Tx) error {
"ON DELETE CASCADE, " +
"FULLTEXT KEY `ip_address_search` (`ip_address`)," +
"UNIQUE KEY `idx_network_interfaces_unique_ip_host_intf` (`ip_address`, `host_id`, `interface`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
return err
}

View file

@ -16,7 +16,7 @@ func Up_20170124230432(tx *sql.Tx) error {
"UNIQUE KEY `idx_unique_email_changes_token` (`token`) USING BTREE, " +
"KEY `fk_email_changes_users` (`user_id`), " +
"CONSTRAINT `fk_email_changes_users` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE " +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;"
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"
_, err := tx.Exec(sqlStatement)
return err
}

View file

@ -3,6 +3,7 @@ package tables
import (
"database/sql"
"fmt"
"os"
"github.com/pkg/errors"
)
@ -14,6 +15,11 @@ func init() {
// changeCharacterSet changes the default character set of the database and all
// table to the provided character set
func changeCharacterSet(tx *sql.Tx, charset string) error {
// This env var should only be set during TestCollation.
if v := os.Getenv("FLEET_TEST_DISABLE_COLLATION_UPDATES"); v != "" {
return nil
}
_, err := tx.Exec(fmt.Sprintf("ALTER DATABASE DEFAULT CHARACTER SET %s", charset))
if err != nil {
return errors.Wrap(err, "alter database")
@ -45,7 +51,7 @@ func changeCharacterSet(tx *sql.Tx, charset string) error {
rows.Close()
for _, name := range names {
_, err = tx.Exec(fmt.Sprintf("ALTER TABLE %s CONVERT TO CHARACTER SET %s", name, charset))
_, err = tx.Exec(fmt.Sprintf("ALTER TABLE %s CONVERT TO CHARACTER SET %s COLLATE utf8mb4_unicode_ci", name, charset))
if err != nil {
return errors.Wrap(err, "alter table "+name)
}

View file

@ -21,7 +21,7 @@ func Up_20171116163618(tx *sql.Tx) error {
"`override_identifier` VARCHAR(255) NOT NULL DEFAULT ''," +
"`options` JSON NOT NULL," +
"PRIMARY KEY (`id`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8;"
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"
_, err := tx.Exec(sqlStatement)
if err != nil {
return errors.Wrap(err, "create table osquery_options")

View file

@ -60,8 +60,8 @@ func Up_20171219164727(tx *sql.Tx) error {
query = `
ALTER TABLE scheduled_queries
ADD COLUMN name varchar(255),
ADD COLUMN description varchar(255)
ADD COLUMN name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
ADD COLUMN description varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
`
if _, err := tx.Exec(query); err != nil {
return errors.Wrap(err, "adding name to scheduled_queries")

View file

@ -11,8 +11,8 @@ func init() {
func Up20200311140000(tx *sql.Tx) error {
_, err := tx.Exec(
"ALTER TABLE `hosts` " +
"ADD COLUMN `primary_ip` varchar(45) NOT NULL DEFAULT ''," +
"ADD COLUMN `primary_mac` varchar(17) NOT NULL DEFAULT ''," +
"ADD COLUMN `primary_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"ADD COLUMN `primary_mac` varchar(17) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''," +
"ADD FULLTEXT KEY `host_ip_mac_search` (`primary_ip`,`primary_mac`)",
)
return err

View file

@ -18,7 +18,7 @@ func Up_20200512120000(tx *sql.Tx) error {
"`secret` VARCHAR(255) NOT NULL," +
"`active` TINYINT(1) DEFAULT TRUE," +
"PRIMARY KEY (`name`)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
)
if err != nil {
return errors.Wrap(err, "create enroll_secrets table")
@ -34,7 +34,7 @@ func Up_20200512120000(tx *sql.Tx) error {
_, err = tx.Exec(
"ALTER TABLE `hosts`" +
"ADD COLUMN `enroll_secret_name` VARCHAR(255) NOT NULL DEFAULT ''",
"ADD COLUMN `enroll_secret_name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''",
)
if err != nil {
return errors.Wrap(err, "drop old secret column")

View file

@ -27,7 +27,7 @@ func Up_20201021104586(tx *sql.Tx) error {
UNIQUE KEY idx_session_id (session_id),
UNIQUE KEY idx_name (name),
FOREIGN KEY (host_id) REFERENCES hosts (id) ON DELETE CASCADE
)`); err != nil {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`); err != nil {
return errors.Wrap(err, "create carve_metadata")
}

View file

@ -19,7 +19,7 @@ func Up_20210421112652(tx *sql.Tx) error {
version varchar(255) NOT NULL DEFAULT '',
source varchar(64) NOT NULL,
UNIQUE KEY idx_name_version (name, version, source)
)`); err != nil {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`); err != nil {
return errors.Wrap(err, "create table software")
}

View file

@ -17,7 +17,7 @@ func Up_20210601000001(tx *sql.Tx) error {
name VARCHAR(255) NOT NULL,
description VARCHAR(1023) NOT NULL DEFAULT '',
UNIQUE KEY idx_name (name)
)`); err != nil {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`); err != nil {
return errors.Wrap(err, "create teams")
}
@ -29,7 +29,7 @@ func Up_20210601000001(tx *sql.Tx) error {
PRIMARY KEY (user_id, team_id),
FOREIGN KEY fk_user_teams_user_id (user_id) REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY fk_user_teams_team_id (team_id) REFERENCES teams (id) ON DELETE CASCADE ON UPDATE CASCADE
)`); err != nil {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`); err != nil {
return errors.Wrap(err, "create user_teams")
}
@ -41,7 +41,7 @@ func Up_20210601000001(tx *sql.Tx) error {
}
if _, err := tx.Exec(`ALTER TABLE users
ADD global_role VARCHAR(64) DEFAULT NULL
ADD global_role VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL
`); err != nil {
return errors.Wrap(err, "alter users")
}

View file

@ -19,7 +19,7 @@ func Up_20210601000002(tx *sql.Tx) error {
PRIMARY KEY (invite_id, team_id),
FOREIGN KEY fk_invite_id (invite_id) REFERENCES invites (id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY fk_team_id (team_id) REFERENCES teams (id) ON DELETE CASCADE ON UPDATE CASCADE
)`); err != nil {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`); err != nil {
return errors.Wrap(err, "create invite_teams")
}

View file

@ -21,7 +21,7 @@ func Up_20210708143152(tx *sql.Tx) error {
user_type VARCHAR(255),
UNIQUE KEY idx_uid_username (host_id, uid, username),
FOREIGN KEY (host_id) REFERENCES hosts (id) ON DELETE CASCADE
)`
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`
if _, err := tx.Exec(sqlStatement); err != nil {
return err
}

View file

@ -2,6 +2,7 @@ package tables
import (
"database/sql"
"github.com/pkg/errors"
)
@ -20,7 +21,7 @@ func Up_20210709124443(tx *sql.Tx) error {
details json DEFAULT NULL,
PRIMARY KEY (id),
FOREIGN KEY fk_activities_user_id (user_id) REFERENCES users (id) ON DELETE SET NULL
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(sql); err != nil {
return errors.Wrap(err, "create activities")

View file

@ -2,6 +2,7 @@ package tables
import (
"database/sql"
"github.com/pkg/errors"
)
@ -17,7 +18,7 @@ func Up_20210712155608(tx *sql.Tx) error {
owner VARCHAR(255),
expires_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY idx_name (name)
)`); err != nil {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`); err != nil {
return errors.Wrap(err, "create locks")
}
return nil

View file

@ -18,7 +18,7 @@ func Up_20210719153709(tx *sql.Tx) error {
updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
anonymous_identifier varchar(255) NOT NULL,
PRIMARY KEY (id)
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(sql); err != nil {
return errors.Wrap(err, "create statistics")

View file

@ -20,7 +20,7 @@ func Up_20210721171531(tx *sql.Tx) error {
cpe varchar(255) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY fk_software_cpe_software_id (software_id) REFERENCES software(id) ON DELETE CASCADE
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(sql); err != nil {
return errors.Wrap(err, "create cpe")

View file

@ -21,7 +21,7 @@ func Up_20210802135933(tx *sql.Tx) error {
PRIMARY KEY (id),
FOREIGN KEY fk_software_cve_cpe_id (cpe_id) REFERENCES software_cpe(id) ON DELETE CASCADE,
UNIQUE KEY unique_cpe_cve(cpe_id, cve)
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(sql); err != nil {
return errors.Wrap(err, "create cve")

View file

@ -19,7 +19,7 @@ func Up_20210819143446(tx *sql.Tx) error {
updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
FOREIGN KEY fk_policies_query_id (query_id) REFERENCES queries(id) ON DELETE RESTRICT
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
policyMembershipHistoryTable := `
CREATE TABLE IF NOT EXISTS policy_membership_history (

View file

@ -20,7 +20,7 @@ func Up_20211013133707(tx *sql.Tx) error {
updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id, type),
INDEX idx_aggregated_stats_updated_at(updated_at)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(aggregatedStatsTable); err != nil {
return errors.Wrap(err, "create aggregated stats table")

View file

@ -18,7 +18,7 @@ func Up_20211216131203(tx *sql.Tx) error {
server_url VARCHAR(255) DEFAULT '' NOT NULL,
installed_from_dep bool DEFAULT FALSE NOT NULL,
PRIMARY KEY (host_id)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(mdmTable); err != nil {
return errors.Wrap(err, "create host_mdm table")
@ -28,7 +28,7 @@ func Up_20211216131203(tx *sql.Tx) error {
host_id int(10) UNSIGNED NOT NULL,
version VARCHAR(255) DEFAULT '' NOT NULL,
PRIMARY KEY (host_id)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(munkiInfoTable); err != nil {
return errors.Wrap(err, "create host_munki_info table")

View file

@ -23,7 +23,7 @@ func Up_20211221110132(tx *sql.Tx) error {
PRIMARY KEY (id),
INDEX idx_host_emails_host_id_email (host_id, email),
INDEX idx_host_emails_email (email)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(hostEmailsTable); err != nil {
return errors.Wrap(err, "create host_emails table")

View file

@ -17,7 +17,7 @@ func Up_20220307104655(tx *sql.Tx) error {
token VARCHAR(255) NOT NULL,
PRIMARY KEY (host_id),
UNIQUE INDEX idx_host_device_auth_token (token)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`
if _, err := tx.Exec(hostDeviceAuthTable); err != nil {
return errors.Wrap(err, "create host_device_auth table")

View file

@ -12,7 +12,7 @@ func init() {
func Up_20220316155700(tx *sql.Tx) error {
_, err := tx.Exec(
"ALTER TABLE `hosts` ADD COLUMN `public_ip` varchar(45) NOT NULL DEFAULT ''",
"ALTER TABLE `hosts` ADD COLUMN `public_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''",
)
if err != nil {
return errors.Wrap(err, "add public_ip column")
@ -20,6 +20,7 @@ func Up_20220316155700(tx *sql.Tx) error {
return nil
}
func Down_20220316155700(tx *sql.Tx) error {
return nil
}

View file

@ -21,7 +21,7 @@ CREATE TABLE jobs (
state VARCHAR(255) NOT NULL,
retries INT NOT NULL DEFAULT 0,
error TEXT
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
if err != nil {
return errors.Wrapf(err, "create table")

View file

@ -17,7 +17,7 @@ CREATE TABLE cve_scores (
cvss_score double,
epss_probability double,
cisa_known_exploit boolean
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
if err != nil {
return errors.Wrapf(err, "create table")

View file

@ -24,7 +24,7 @@ CREATE TABLE host_batteries (
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY idx_host_batteries_host_id_serial_number (host_id, serial_number)
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
if err != nil {
return errors.Wrapf(err, "create table")
}

View file

@ -21,7 +21,7 @@ CREATE TABLE operating_systems (
kernel_version VARCHAR(150) NOT NULL,
platform VARCHAR(50) NOT NULL,
UNIQUE KEY idx_unique_os (name, version, arch, kernel_version, platform)
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
if err != nil {
return errors.Wrapf(err, "create operating_systems table")

View file

@ -22,7 +22,7 @@ CREATE TABLE mobile_device_management_solutions (
updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY idx_mobile_device_management_solutions_name (name, server_url)
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
if err != nil {
return errors.Wrapf(err, "create table")
}

View file

@ -28,7 +28,7 @@ func Up_20220822161445(tx *sql.Tx) error {
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY idx_munki_issues_name (name, issue_type)
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
if err != nil {
return errors.Wrapf(err, "create munki_issues table")
}

View file

@ -26,8 +26,8 @@ func TestUp_20220908181826(t *testing.T) {
require.NoError(t, err)
applyNext(t, db)
sqlUpdate := `UPDATE hosts SET orbit_node_key = ? WHERE osquery_host_id = ?`
_, err = db.Exec(sqlUpdate, "orbit_node_key", "host_id")
require.NoError(t, err)
}

View file

@ -52,7 +52,7 @@ CREATE TABLE IF NOT EXISTS mdm_apple_enrollment_profiles (
PRIMARY KEY (id),
UNIQUE KEY idx_token (token),
UNIQUE KEY idx_type (type)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
if err != nil {
return fmt.Errorf("failed to create mdm_apple_enrollments table: %w", err)
@ -68,7 +68,7 @@ CREATE TABLE IF NOT EXISTS mdm_apple_installers (
url_token VARCHAR(36) DEFAULT NULL,
PRIMARY KEY (id)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
if err != nil {
return fmt.Errorf("failed to create mdm_apple_installers table: %w", err)

View file

@ -33,4 +33,4 @@ CREATE TABLE nano_dep_names (
CHECK (tokenpki_cert_pem IS NULL OR SUBSTRING(tokenpki_cert_pem FROM 1 FOR 27) = '-----BEGIN CERTIFICATE-----'),
CHECK (tokenpki_key_pem IS NULL OR SUBSTRING(tokenpki_key_pem FROM 1 FOR 5) = '-----')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View file

@ -3,7 +3,7 @@
*/
CREATE TABLE nano_devices (
id VARCHAR(255) NOT NULL,
id VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
identity_cert TEXT NULL,
@ -41,7 +41,7 @@ CREATE TABLE nano_devices (
CHECK (token_update IS NULL OR token_update != ''),
CHECK (bootstrap_token_b64 IS NULL OR bootstrap_token_b64 != '')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE nano_users (
@ -77,7 +77,7 @@ CREATE TABLE nano_users (
CHECK (user_authenticate IS NULL OR user_authenticate != ''),
CHECK (user_authenticate_digest IS NULL OR user_authenticate_digest != '')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE nano_users ADD CONSTRAINT idx_unique_id UNIQUE (id);
@ -86,7 +86,7 @@ ALTER TABLE nano_users ADD CONSTRAINT idx_unique_id UNIQUE (id);
*/
CREATE TABLE nano_enrollments (
-- The enrollment ID of this enrollment
id VARCHAR(255) NOT NULL,
id VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
-- The "device" enrollment ID of this enrollment. This will be
-- the same as the `id` field in the case of a "device" enrollment,
-- or will be the "parent" enrollment for a "user" enrollment.
@ -130,7 +130,7 @@ CREATE TABLE nano_enrollments (
CHECK (topic != ''),
CHECK (push_magic != ''),
CHECK (token_hex != '')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/* Commands stand alone. By themsevles they aren't associated with
@ -151,7 +151,7 @@ CREATE TABLE nano_commands (
CHECK (command_uuid != ''),
CHECK (request_type != ''),
CHECK (SUBSTRING(command FROM 1 FOR 5) = '<?xml')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/* Results are enrollment responses to device commands.
@ -192,7 +192,7 @@ CREATE TABLE nano_command_results (
CHECK (status != ''),
INDEX (status),
CHECK (SUBSTRING(result FROM 1 FOR 5) = '<?xml')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE nano_enrollment_queue (
@ -214,7 +214,7 @@ CREATE TABLE nano_enrollment_queue (
FOREIGN KEY (command_uuid)
REFERENCES nano_commands (command_uuid)
ON DELETE CASCADE ON UPDATE CASCADE
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/* An enrollment's queue is a view into commands, enrollment queued
* commands, and any results received. Outstanding queue items (i.e.
@ -268,7 +268,7 @@ CREATE TABLE nano_push_certs (
CHECK (SUBSTRING(cert_pem FROM 1 FOR 27) = '-----BEGIN CERTIFICATE-----'),
CHECK (SUBSTRING(key_pem FROM 1 FOR 5) = '-----')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE nano_cert_auth_associations (
@ -282,4 +282,4 @@ CREATE TABLE nano_cert_auth_associations (
CHECK (id != ''),
CHECK (sha256 != '')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View file

@ -37,4 +37,4 @@ CREATE TABLE IF NOT EXISTS scep_certificates (
CHECK (SUBSTRING(certificate_pem FROM 1 FOR 27) = '-----BEGIN CERTIFICATE-----'),
CHECK (name IS NULL OR name != '')
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

View file

@ -2,6 +2,7 @@ package tables
import (
"database/sql"
"github.com/pkg/errors"
)
@ -19,7 +20,7 @@ func Up_20220915165116(tx *sql.Tx) error {
display_name varchar(255) NOT NULL,
PRIMARY KEY (host_id),
KEY (display_name)
);
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`},
{"migrate data", `
INSERT INTO host_display_names (
@ -30,7 +31,6 @@ func Up_20220915165116(tx *sql.Tx) error {
if _, err := tx.Exec(change.sql); err != nil {
return errors.Wrapf(err, "upHostDisplayName: %s", change.name)
}
}
return nil
}

View file

@ -16,7 +16,7 @@ func Up_20221014084130(tx *sql.Tx) error {
PRIMARY KEY (host_id),
KEY idx_host_orbit_info_version (version)
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
return err
}

View file

@ -25,7 +25,7 @@ func Up_20221027085019(tx *sql.Tx) error {
UNIQUE KEY idx_operating_system_vulnerabilities_unq_cve (host_id, cve),
INDEX idx_operating_system_vulnerabilities_operating_system_id_cve (operating_system_id, cve)
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
if err != nil {
return errors.Wrapf(err, "operating_system_vulnerabilities")

View file

@ -20,7 +20,9 @@ func Up_20221115104546(tx *sql.Tx) error {
status VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_cron_stats_name_created_at (name, created_at))
INDEX idx_cron_stats_name_created_at (name, created_at)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`)
if err != nil {
return errors.Wrapf(err, "create table")

View file

@ -12,7 +12,7 @@ func init() {
func Up_20221223174807(tx *sql.Tx) error {
_, err := tx.Exec(`
ALTER TABLE hosts MODIFY osquery_host_id VARCHAR(255) NULL;
ALTER TABLE hosts MODIFY osquery_host_id VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL;
ALTER TABLE hosts ADD INDEX idx_hosts_hardware_serial (hardware_serial)
`)
if err != nil {

View file

@ -24,7 +24,7 @@ func Up_20230202224725(tx *sql.Tx) error {
PRIMARY KEY (host_id),
KEY idx_host_disk_encryption_keys_decryptable (decryptable)
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
return err
}

View file

@ -25,7 +25,7 @@ CREATE TABLE mdm_apple_configuration_profiles (
PRIMARY KEY (profile_id),
UNIQUE KEY idx_mdm_apple_config_prof_team_identifier (team_id, identifier),
UNIQUE KEY idx_mdm_apple_config_prof_team_name (team_id, name)
);`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
if err != nil {
return errors.Wrapf(err, "create table")
}

View file

@ -12,7 +12,7 @@ func Up_20230214131519(tx *sql.Tx) error {
_, err := tx.Exec(`
CREATE TABLE mdm_apple_delivery_status (
status VARCHAR(20) PRIMARY KEY
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`)
if err != nil {
return err
}
@ -28,7 +28,7 @@ func Up_20230214131519(tx *sql.Tx) error {
_, err = tx.Exec(`
CREATE TABLE mdm_apple_operation_types (
operation_type VARCHAR(20) PRIMARY KEY
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci`)
if err != nil {
return err
}
@ -54,7 +54,7 @@ func Up_20230214131519(tx *sql.Tx) error {
PRIMARY KEY (host_uuid, profile_id),
FOREIGN KEY (status) REFERENCES mdm_apple_delivery_status (status) ON UPDATE CASCADE,
FOREIGN KEY (operation_type) REFERENCES mdm_apple_operation_types (operation_type) ON UPDATE CASCADE
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
return err
}

View file

@ -18,7 +18,7 @@ func Up_20230303135738(tx *sql.Tx) error {
iterations int unsigned NOT NULL,
PRIMARY KEY (uuid)
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
return err
}

View file

@ -11,7 +11,7 @@ func init() {
}
func Up_20230313135301(tx *sql.Tx) error {
if _, err := tx.Exec("ALTER TABLE `host_mdm_apple_profiles` ADD COLUMN `profile_name` varchar(255) NOT NULL DEFAULT ''"); err != nil {
if _, err := tx.Exec("ALTER TABLE `host_mdm_apple_profiles` ADD COLUMN `profile_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''"); err != nil {
return errors.Wrap(err, "adding profile_name column to `host_mdm_apple_profiles")
}
return nil

View file

@ -5,6 +5,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"os"
"text/template"
"github.com/jmoiron/sqlx"
@ -36,7 +37,6 @@ func fixupSoftware(tx *sql.Tx, collation string) error {
arch COLLATE %s
HAVING total > 1
COLLATE %s`, collation, collation, collation, collation, collation))
if err != nil {
return fmt.Errorf("aggregating dupes: %w", err)
}
@ -208,6 +208,11 @@ func fixupOS(tx *sql.Tx, collation string) error {
// This is based on the changeCharacterSet function that's included in this
// module and part of the 20170306075207_UseUTF8MB migration.
func changeCollation(tx *sql.Tx, charset string, collation string) (err error) {
// This env var should only be set during TestCollation.
if v := os.Getenv("FLEET_TEST_DISABLE_COLLATION_UPDATES"); v != "" {
return nil
}
_, err = tx.Exec(fmt.Sprintf("ALTER DATABASE DEFAULT CHARACTER SET `%s` COLLATE `%s`", charset, collation))
if err != nil {
return fmt.Errorf("alter database: %w", err)
@ -228,11 +233,11 @@ func changeCollation(tx *sql.Tx, charset string, collation string) (err error) {
txx := sqlx.Tx{Tx: tx}
var names []string
err = txx.Select(&names, `
SELECT table_name
FROM information_schema.TABLES AS T, information_schema.COLLATION_CHARACTER_SET_APPLICABILITY AS C
WHERE C.collation_name = T.table_collation
AND T.table_schema = (SELECT database())
AND (C.CHARACTER_SET_NAME != ? OR C.COLLATION_NAME != ?)
SELECT table_name
FROM information_schema.TABLES AS T, information_schema.COLLATION_CHARACTER_SET_APPLICABILITY AS C
WHERE C.collation_name = T.table_collation
AND T.table_schema = (SELECT database())
AND (C.CHARACTER_SET_NAME != ? OR C.COLLATION_NAME != ?)
-- exclude tables that have columns with specific collations
AND table_name NOT IN ('hosts', 'enroll_secrets')`, charset, collation)
if err != nil {

View file

@ -13,8 +13,8 @@ func Up_20230411102858(tx *sql.Tx) error {
// create host_mdm_apple_bootstrap_packages table
_, err := tx.Exec(`
CREATE TABLE host_mdm_apple_bootstrap_packages (
host_uuid varchar(127) NOT NULL,
command_uuid varchar(127) NOT NULL,
host_uuid varchar(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
command_uuid varchar(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (host_uuid),
FOREIGN KEY (command_uuid) REFERENCES nano_commands (command_uuid) ON DELETE CASCADE

View file

@ -15,7 +15,7 @@ func Up_20230518114155(tx *sql.Tx) error {
DROP COLUMN salt,
DROP COLUMN entropy,
DROP COLUMN iterations,
ADD COLUMN fullname varchar(256) NOT NULL DEFAULT ''
ADD COLUMN fullname varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''
`
if _, err := tx.Exec(stmt); err != nil {
return fmt.Errorf("alter mdm_idp_accounts table: %w", err)

View file

@ -29,12 +29,12 @@ func Up_20230721135421(tx *sql.Tx) error {
ADD team_id INT(10) UNSIGNED DEFAULT NULL,
ADD team_id_char CHAR(10) DEFAULT '' NOT NULL,
ADD platform VARCHAR(255) DEFAULT '' NOT NULL,
ADD min_osquery_version VARCHAR(255) DEFAULT '' NOT NULL,
ADD platform VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' NOT NULL,
ADD min_osquery_version VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' NOT NULL,
ADD schedule_interval INT(10) UNSIGNED DEFAULT 0 NOT NULL,
ADD automations_enabled TINYINT(1) UNSIGNED DEFAULT 0 NOT NULL,
ADD logging_type VARCHAR(255) DEFAULT 'snapshot' NOT NULL,
ADD logging_type VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT 'snapshot' NOT NULL,
ADD FOREIGN KEY fk_queries_team_id (team_id) REFERENCES teams (id) ON DELETE CASCADE,
ADD UNIQUE INDEX idx_team_id_name_unq (team_id_char, name);

View file

@ -3,6 +3,7 @@ package tables
import (
"database/sql"
"fmt"
"os"
"github.com/jmoiron/sqlx"
)
@ -17,6 +18,11 @@ func init() {
// This is based on the changeCollation function that's included in this
// module and part of the 20230315104937_EnsureUniformCollation migration.
func changeCollation2025(tx *sql.Tx, charset string, collation string) (err error) {
// This env var should only be set during TestCollation.
if v := os.Getenv("FLEET_TEST_DISABLE_COLLATION_UPDATES"); v != "" {
return nil
}
_, err = tx.Exec(fmt.Sprintf("ALTER DATABASE DEFAULT CHARACTER SET `%s` COLLATE `%s`", charset, collation))
if err != nil {
return fmt.Errorf("alter database: %w", err)
@ -25,11 +31,11 @@ func changeCollation2025(tx *sql.Tx, charset string, collation string) (err erro
txx := sqlx.Tx{Tx: tx}
var names []string
err = txx.Select(&names, `
SELECT table_name
FROM information_schema.TABLES AS T, information_schema.COLLATION_CHARACTER_SET_APPLICABILITY AS C
WHERE C.collation_name = T.table_collation
AND T.table_schema = (SELECT database())
AND (C.CHARACTER_SET_NAME != ? OR C.COLLATION_NAME != ?)
SELECT table_name
FROM information_schema.TABLES AS T, information_schema.COLLATION_CHARACTER_SET_APPLICABILITY AS C
WHERE C.collation_name = T.table_collation
AND T.table_schema = (SELECT database())
AND (C.CHARACTER_SET_NAME != ? OR C.COLLATION_NAME != ?)
-- exclude tables that have columns with specific collations
AND table_name NOT IN ('hosts', 'enroll_secrets')`, charset, collation)
if err != nil {

View file

@ -12,9 +12,9 @@ func init() {
func Up_20250501162727(tx *sql.Tx) error {
_, err := tx.Exec(`CREATE TABLE IF NOT EXISTS software_categories (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
name VARCHAR(63) NOT NULL,
UNIQUE KEY idx_software_categories_name (name)
)`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;`)
if err != nil {
return fmt.Errorf("failed to create software_categories table: %w", err)
}

View file

@ -0,0 +1,33 @@
package tables
import (
"database/sql"
"fmt"
)
func init() {
MigrationClient.AddMigration(Up_20250529102706, Down_20250529102706)
}
func Up_20250529102706(tx *sql.Tx) error {
_, err := tx.Exec(
`ALTER TABLE queries
MODIFY query MEDIUMTEXT NOT NULL,
MODIFY description MEDIUMTEXT NOT NULL;
`,
)
if err != nil {
return fmt.Errorf("failed to set queries.query and queries.description to mediumtext: %w", err)
}
_, err = tx.Exec(
`ALTER TABLE labels MODIFY query MEDIUMTEXT NOT NULL;`,
)
if err != nil {
return fmt.Errorf("failed to set labels.query to mediumtext: %w", err)
}
return nil
}
func Down_20250529102706(tx *sql.Tx) error {
return nil
}

View file

@ -0,0 +1,31 @@
package tables
import (
"errors"
"os"
"testing"
"github.com/fleetdm/fleet/v4/server/goose"
"github.com/stretchr/testify/require"
)
func TestCollation(t *testing.T) {
require.NoError(t, os.Setenv("FLEET_TEST_DISABLE_COLLATION_UPDATES", "true"))
t.Cleanup(func() {
require.NoError(t, os.Unsetenv("FLEET_TEST_DISABLE_COLLATION_UPDATES"))
})
db := newDBConnForTests(t)
for {
current, err := MigrationClient.GetDBVersion(db.DB)
require.NoError(t, err)
_, err = MigrationClient.Migrations.Next(current)
if errors.Is(err, goose.ErrNoNextVersion) {
break
}
require.NoError(t, err)
applyNext(t, db)
}
checkCollation(t, db)
}

View file

@ -155,6 +155,36 @@ func insertQuery(t *testing.T, db *sqlx.DB) uint {
return uint(id) //nolint:gosec // dismiss G115
}
func checkCollation(t *testing.T, db *sqlx.DB) {
type collationData struct {
CollationName string `db:"COLLATION_NAME"`
TableName string `db:"TABLE_NAME"`
ColumnName string `db:"COLUMN_NAME"`
CharacterSetName string `db:"CHARACTER_SET_NAME"`
}
stmt := `
SELECT
TABLE_NAME, COLLATION_NAME, COLUMN_NAME, CHARACTER_SET_NAME
FROM information_schema.columns
WHERE
TABLE_SCHEMA = (SELECT DATABASE())
AND (CHARACTER_SET_NAME != ? OR COLLATION_NAME != ?)`
var nonStandardCollations []collationData
err := db.Select(&nonStandardCollations, stmt, "utf8mb4", "utf8mb4_unicode_ci")
require.NoError(t, err)
exceptions := []collationData{
{"utf8mb4_bin", "enroll_secrets", "secret", "utf8mb4"},
{"utf8mb4_bin", "hosts", "node_key", "utf8mb4"},
{"utf8mb4_bin", "hosts", "orbit_node_key", "utf8mb4"},
{"utf8mb4_bin", "teams", "name_bin", "utf8mb4"},
}
require.ElementsMatch(t, exceptions, nonStandardCollations)
}
func insertHost(t *testing.T, db *sqlx.DB, teamID *uint) uint {
// Insert a minimal record into hosts table
insertHostStmt := `

File diff suppressed because one or more lines are too long