PT-ONLI...A-CHANGE(1)
π pt-online-schema-change π
User Contributed Perl Documentation β PT-ONLI...A-CHANGE(1). Bestie, this is the whole vibe: alter tables without locking them.
π NAME π
pt-online-schema-change β ALTER tables without locking them. Periodt.
β¨ SYNOPSIS β¨
Usage: pt-online-schema-change [OPTIONS] DSN
pt-online-schema-change alters a table's structure without blocking reads or writes. You specify the database and table in the DSN. And listen: do not use this tool before reading its documentation and checking your backups carefully. Like, actually check them. π
Add a column to sakila.actor:
pt-online-schema-change --alter "ADD COLUMN c1 INT" D=sakila,t=actor
Change sakila.actor to InnoDB, effectively performing OPTIMIZE TABLE in a non-blocking fashion because it is already an InnoDB table:
pt-online-schema-change --alter "ENGINE=InnoDB" D=sakila,t=actor
π RISKS π
Percona Toolkit is mature, proven in the real world, and well tested, but all database tools can pose a risk to the system and the database server. Before using this tool, please:
- Read the tool's documentation
- Review the tool's known βBUGSβ
- Test the tool on a non-production server
- Backup your production server and verify the backups
π«Ά No cap: backups are not a vibe check you skip. Verify them.
πΈ DESCRIPTION πΈ
pt-online-schema-change emulates the way that MySQL alters tables internally, but it works on a copy of the table you wish to alter. That means the original table is not locked, and clients may continue to read and change data in it. Iconic behaviour, honestly.
pt-online-schema-change works by creating an empty copy of the table to alter, modifying it as desired, and then copying rows from the original table into the new table. When the copy is complete, it moves away the original table and replaces it with the new one. By default, it also drops the original table. π
The data copy process is performed in small chunks of data, which are varied to attempt to make them execute in a specific amount of time (see --chunk-time). This process is very similar to how other tools, such as pt-table-checksum, work. Any modifications to data in the original tables during the copy will be reflected in the new table, because the tool creates triggers on the original table to update the corresponding rows in the new table. The use of triggers means that the tool will not work if any triggers are already defined on the table. π
When the tool finishes copying data into the new table, it uses an atomic RENAME TABLE operation to simultaneously rename the original and new tables. After this is complete, the tool drops the original table.
Foreign keys complicate the tool's operation and introduce additional risk. The technique of atomically renaming the original and new tables does not work when foreign keys refer to the table. The tool must update foreign keys to refer to the new table after the schema change is complete. The tool supports two methods for accomplishing this. You can read more about this in the documentation for --alter-foreign-keys-method.
Foreign keys also cause some side effects. The final table will have the same foreign keys and indexes as the original table (unless you specify differently in your ALTER statement), but the names of the objects may be changed slightly to avoid object name collisions in MySQL and InnoDB.
For safety, the tool does not modify the table unless you specify the --execute option, which is not enabled by default. The tool supports a variety of other measures to prevent unwanted load or other problems, including automatically detecting replicas, connecting to them, and using the following safety checks:
- In most cases the tool will refuse to operate unless a PRIMARY KEY or UNIQUE INDEX is present in the table. See
--alterfor details. - The tool refuses to operate if it detects replication filters. See
--[no]check-replication-filtersfor details. - The tool pauses the data copy operation if it observes any replicas that are delayed in replication. See
--max-lagfor details. - The tool pauses or aborts its operation if it detects too much load on the server. See
--max-loadand--critical-loadfor details. - The tool sets
innodb_lock_wait_timeout=1and (for MySQL 5.5 and newer)lock_wait_timeout=60so that it is more likely to be the victim of any lock contention, and less likely to disrupt other transactions. These values can be changed by specifying--set-vars. - The tool refuses to alter the table if foreign key constraints reference it, unless you specify
--alter-foreign-keys-method. - The tool cannot alter MyISAM tables on
Percona XtraDB Clusternodes.
π Percona XtraDB Cluster π
pt-online-schema-change works with Percona XtraDB Cluster (PXC) 5.5.28-23.7 and newer, but there are two limitations: only InnoDB tables can be altered, and wsrep_OSU_method must be set to TOI (total order isolation). The tool exits with an error if the host is a cluster node and the table is MyISAM or is being converted to MyISAM (ENGINE=MyISAM), or if wsrep_OSU_method is not TOI. There is no way to disable these checks. π¨
π§ MySQL 5.7 + Generated columns π§
The tool ignores MySQL 5.7+ GENERATED columns since the value for those columns is generated according to the expression used to compute column values.
β OUTPUT β
The tool prints information about its activities to STDOUT so that you can see what it is doing. During the data copy phase, it prints --progress reports to STDERR. You can get additional information by specifying --print.
If --statistics is specified, a report of various internal event counts is printed at the end, like:
# Event Count
# ====== =====
# INSERT 1
π OPTIONS π
--dry-run and --execute are mutually exclusive.
This tool accepts additional command-line arguments. Refer to the SYNOPSIS and usage information for details.
--alterβ type:string
The schema modification, without the ALTER TABLE keywords. You can perform multiple modifications to the table by specifying them with commas. Please refer to the MySQL manual for the syntax of ALTER TABLE.
The following limitations apply which, if attempted, will cause the tool to fail in unpredictable ways:
- In almost all cases a PRIMARY KEY or UNIQUE INDEX needs to be present in the table. This is necessary because the tool creates a DELETE trigger to keep the new table updated while the process is running.
A notable exception is when a PRIMARY KEY or UNIQUE INDEX is being created from existing columns as part of the ALTER clause; in that case it will use these column(s) for the DELETE trigger.
- The
RENAMEclause cannot be used to rename the table. - Columns cannot be renamed by dropping and re-adding with the new name. The tool will not copy the original column's data to the new column.
- If you add a column without a default value and make it NOT NULL, the tool will fail, as it will not try to guess a default value for you; You must specify the default.
DROP FOREIGN KEY constraint_namerequires specifying_constraint_namerather than the realconstraint_name. Due to a limitation in MySQL,pt-online-schema-changeadds a leading underscore to foreign key constraint names when creating the new table. For example, to drop this constraint:
CONSTRAINT `fk_foo` FOREIGN KEY (`foo_id`) REFERENCES `bar` (`foo_id`)
You must specify --alter "DROP FOREIGN KEY _fk_foo".
- The tool does not use
LOCK IN SHARE MODEwith MySQL 5.0 because it can cause a replica error which breaks replication:
Query caused different errors on master and slave. Error on master:
'Deadlock found when trying to get lock; try restarting transaction' (1213),
Error on slave: 'no error' (0). Default database: 'pt_osc'.
Query: 'INSERT INTO pt_osc.t (id, c) VALUES ('730', 'new row')'
The error happens when converting a MyISAM table to InnoDB because MyISAM is non-transactional but InnoDB is transactional. MySQL 5.1 and newer handle this case correctly, but testing reproduces the error 5% of the time with MySQL 5.0.
This is a MySQL bug, similar to http://bugs.mysql.com/bug.php?id=45694, but there is no fix or workaround in MySQL 5.0. Without LOCK IN SHARE MODE, tests pass 100% of the time, so the risk of data loss or breaking replication should be negligible.
Be sure to verify the new table if using MySQL 5.0 and converting from MyISAM to InnoDB!
--alter-foreign-keys-methodβ type:string
How to modify foreign keys so they reference the new table. Foreign keys that reference the table to be altered must be treated specially to ensure that they continue to reference the correct table. When the tool renames the original table to let the new one take its place, the foreign keys βfollowβ the renamed table, and must be changed to reference the new table instead.
The tool supports two techniques to achieve this. It automatically finds βchild tablesβ that reference the table to be altered.
autoβ Automatically determine which method is best. The tool usesrebuild_constraintsif possible (see the description of that method for details), and if not, then it usesdrop_swap.rebuild_constraintsβ This method usesALTER TABLEto drop and re-add foreign key constraints that reference the new table. This is the preferred technique, unless one or more of the βchildβ tables is so large that theALTERwould take too long. The tool determines that by comparing the number of rows in the child table to the rate at which the tool is able to copy rows from the old table to the new table. If the tool estimates that the child table can be altered in less time than the--chunk-time, then it will use this technique. For purposes of estimating the time required to alter the child table, the tool multiplies the row-copying rate by--chunk-size-limit, because MySQL'sALTER TABLEis typically much faster than the external process of copying rows.
Due to a limitation in MySQL, foreign keys will not have the same names after the ALTER that they did prior to it. The tool has to rename the foreign key when it redefines it, which adds a leading underscore to the name. In some cases, MySQL also automatically renames indexes required for the foreign key.
drop_swapβ this one turns off foreign key checks (FOREIGN_KEY_CHECKS=0), drops the original table, then renames the new table into its spot. It's not like the normal swap method, where old and new tables get swapped with an atomicRENAMEthat client apps straight up can't detect π«₯
It's faster and doesn't block, but it's riskier, two reasons, no cap. One: for a hot sec between dropping the original and renaming the temp table, the table you're altering literally does not exist, so any query against it errors out. Two: if something goes wrong and the new table can't be renamed into the old one's place, it's too late to abort β the old table is gone permanently π
This method forces --no-swap-tables and --no-drop-old-table.
noneβ this is likedrop_swapminus the swap. Any foreign keys that pointed at the original table now point at a nonexistent table. Which typically causes foreign key violations you can see inSHOW ENGINE INNODB STATUS, kinda like this:
Trying to add to index `idx_fk_staff_id` tuple:
DATA TUPLE: 2 fields;
0: len 1; hex 05; asc ;;
1: len 4; hex 80000001; asc ;;
But the parent table `sakila`.`staff_old`
or its .ibd file does not currently exist!
Why? Because the original table (in this case, sakila.staff) got renamed to sakila.staff_old and then dropped. This whole method of handling foreign key constraints exists so the DBA can switch off the tool's built-in functionality if they want to ποΈ
--[no]analyze-before-swapβ default:yes
Runs ANALYZE TABLE on the new table before swapping it with the old one. By default this only happens on MySQL 5.6 and newer, and when innodb_stats_persistent is enabled. Say the option explicitly and it's on or off regardless of MySQL version and innodb_stats_persistent β¨
This dodges a potentially serious issue with InnoDB optimizer statistics. If the table being altered is busy and the tool finishes quickly, the new table won't have optimizer statistics after the swap. Then fast, index-using queries can do full table scans until optimizer stats get updated (usually after 10 seconds). If the table is large and the server is very busy, that can straight up cause an outage π
--ask-passβ prompts for a password when connecting to MySQL π--binary-indexβ this one changes what--historydoes so the history table's upper and lower boundary columns get created with theBLOBdata type. Super useful when you're changing big tables with keys that include a binary data type, or that have non-standard character sets. Peep--historyand--resumeπ--[no]buffer-stdoutβ default:yes
Turns STDOUT buffering on. Disable it if you wanna see live progress while using output post-processing tools like tee or kubectl logs π«Ά
--channelβ type:string
Channel name used when you're connected to a server with replication channels. Say you've got two replication sources, source_a on port 12345 and source_b on port 1236, plus a replica connected to both via channels chan_source_a and chan_source_b. You'd use --channel=chan_source_a to name the channel used in the SHOW REPLICA STATUS command that pt-online-schema-change uses to calculate replication lag π
--charsetβ short form:-A; type:string
Default character set. If the value is utf8, it sets Perl's binmode on STDOUT to utf8, passes the mysql_enable_utf8 option to DBD::mysql, and runs SET NAMES UTF8 after connecting to MySQL. Any other value sets binmode on STDOUT without the utf8 layer, and runs SET NAMES after connecting to MySQL π
--[no]check-alterβ default:yes
Parses the --alter you gave it and tries to warn you about possible unintended behavior. Right now it checks for:
- Column renames β in previous versions of the tool, renaming a column with
CHANGE COLUMN name new_namewould straight up lose that column's data π± The tool now parses the alter statement and tries to catch those cases, so renamed columns should keep the same data as the originals. But the code doing this is not a full-blown SQL parser, so run the tool first with--dry-runand--printand verify it detects the renamed columns correctly. - DROP PRIMARY KEY β if
--altercontainsDROP PRIMARY KEY(case- and space-insensitive), a warning gets printed and the tool exits, unless--dry-runis specified. Altering the primary key can be dangerous, but the tool can handle it. The tool's triggers, especially the DELETE trigger, are the most affected by altering the primary key, because the tool prefers to use the primary key for its triggers. Run it first with--dry-runand--printand verify the triggers are correct π
--[no]check-foreign-keysβ default:yes
Checks for self-referencing foreign keys. RN self-referencing FKs aren't fully supported, so to prevent errors this program won't run if the table has self-referencing foreign keys. Use this parameter to disable those self-referencing FK checks π«
--check-intervalβ type:time; default:1
Sleep time between checks for --max-lag π΄
--[no]check-planβ default:yes
Checks query execution plans for safety. By default this makes the tool run EXPLAIN before running queries that are meant to touch a small amount of data β but which could touch a lot of rows if MySQL picks a bad execution plan. That includes the queries figuring out chunk boundaries and the chunk queries themselves. If it looks like MySQL will use a bad plan, the tool stops copying rows and dies π
It uses a few heuristics to decide if a plan is bad. First: whether EXPLAIN reports that MySQL intends to use the desired index to access the rows. If MySQL picks a different index, the tool considers the query unsafe.
It also checks how much of the index MySQL reports it'll use for the query. EXPLAIN output shows that in the key_len column. The tool remembers the largest key_len it's seen, and terminates if MySQL reports it'll use a smaller prefix of the index.
This option adds some setup work to each table and chunk. It's not intrusive for MySQL, but it means more round-trips to the server, which eats time. Make chunks too small and that overhead becomes relatively bigger. So pls don't make chunks too small, 'cause the tool may take a very long time to finish if you do π
--[no]check-replication-filtersβ default:yes
Aborts if any replication filter is set on any server. The tool looks for server options that filter replication, like binlog_ignore_db and replicate_do_db. Find any of those and it aborts with an error β οΈ
If your replicas are configured with any filtering options, be careful not to modify databases or tables that exist on the replication source but not on the replicas, because that could cause replication to fail. More on replication rules here: <http://dev.mysql.com/doc/en/replication-rules.html>
--check-replica-lagβ type:string
Pauses the data copy until this replica's lag is less than --max-lag. The value is a DSN that inherits properties from the connection options (--port, --user, etc.). This option overrides the normal behavior of finding and continually monitoring replication lag on ALL connected replicas. If you don't wanna monitor ALL replicas but you want more than just one monitored, then use the DSN option to --recursion-method instead of this one π§
--check-slave-lagβ type:string
This option is deprecated and will be removed in future releases. Use --check-replica-lag instead π«‘
--chunk-indexβ type:string
Prefer this index for chunking tables. By default the tool picks the most appropriate index for chunking; this option lets you say which one you prefer. If the index doesn't exist, the tool falls back to its default choosing behavior. It adds the index to the SQL statements in a FORCE INDEX clause. Be careful with this one β a poor index choice could tank performance π
--chunk-index-columnsβ type:int
Use only this many left-most columns of a --chunk-index. Only works for compound indexes, and it's useful when a bug in the MySQL query optimizer (planner) makes it scan a huge range of rows instead of using the index to nail down starting and ending points precisely. This sometimes happens on indexes with lots of columns, like 4 or more. If it does, the tool might print a warning related to the --[no]check-plan option. Telling the tool to use only the first N columns of the index is a workaround for the bug in some cases π§
--chunk-sizeβ type:size; default:1000
Number of rows to select for each chunk copied. Allowable suffixes are k, M, G.
This option can override the default behavior, which is to adjust chunk size dynamically to try to make chunks run in exactly --chunk-time seconds. When this option isn't set explicitly, its default value is used as a starting point β but after that, the tool ignores this option's value. If you set this option explicitly and don't set --chunk-time explicitly, then it disables the dynamic adjustment behavior and tries to make all chunks exactly the number of rows you specified π―
--chunk-size-limitβ type:float; default:4.0
Do not copy chunks this much larger than the desired chunk size π ββοΈ
When a table's got no unique indexes, the chunk sizes can come out kinda inaccurate, and that's just facts. This option sets a max tolerable limit on that inaccuracy. The tool uses EXPLAIN to estimate how many rows are in the chunk. If that estimate goes over the desired chunk size times the limit, then the tool stops copying rows and straight up dies. π
The minimum value for this option is 1, which means no chunk can be bigger than --chunk-size. That said, you probably don't want to specify 1, because rows reported by EXPLAIN are estimates, which can differ from the real number of rows in the chunk. You can disable oversized chunk checking by specifying a value of 0.
The tool also uses this option to determine how to handle foreign keys that reference the table to be altered. See --alter-foreign-keys-method for details.
--chunk-timeβ type: float; default:0.5
Adjust the chunk size dynamically so each data-copy query takes this long to execute. The tool tracks the copy rate (rows per second) and adjusts the chunk size after each data-copy query, so that the next query takes this amount of time (in seconds) to execute. It keeps an exponentially decaying moving average of queries per second, so that if the server's performance changes because of changes in server load, the tool adapts quickly. It's giving adaptive queen energy honestly. β¨
If this option is set to zero, the chunk size doesn't auto-adjust, so query times will vary, but query chunk sizes won't. Another way to do the same thing is to specify a value for --chunk-size explicitly, instead of leaving it at the default, and omit the option --chunk-time.
If both options --chunk-size and --chunk-time are set explicitly, the initial chunk size will be as specified by the option --chunk-size, but later it will be adjusted, so that the next query takes this amount of time (in seconds) to execute. Use this option if the tool spends too much time copying the first chunk with the default --chunk-size.
--configβ type: Array
Read this comma-separated list of config files; if specified, this must be the first option on the command line. like, first first. no cutting in line π
--history-tableβ type: string; default:percona.pt_osc_history
Create the --history database and table if they don't already exist. The structure of the history table is the same as the suggested table mentioned in --history.
By default, --history creates the database and the table specified by this option automatically if they don't exist. self-sufficient icon behavior fr π
--critical-loadβ type: Array; default:Threads_running=50
Examine SHOW GLOBAL STATUS after every chunk, and abort if the load is too high. The option accepts a comma-separated list of MySQL status variables and thresholds. An optional =MAX_VALUE (or :MAX_VALUE) can follow each variable. If it's not given, the tool determines a threshold by examining the current value at startup and doubling it.
See --max-load for further details. These two options work similarly, except that this option will abort the tool's operation instead of pausing it, and the default value is computed differently if you specify no threshold. The reason this option exists at all is as a safety check in case the triggers on the original table add so much load to the server that it causes downtime. There's probably no single value of Threads_running that's wrong for every server, but a default of 50 seems likely to be unacceptably high for most servers, which is basically the tool telling you "yeah that's a red flag, cancel immediately." π©
--databaseβ short form:-D; type: string
Connect to this database.
--default-engine
Remove ENGINE from the new table.
By default the new table is created with the same table options as the original table, so if the original table uses InnoDB, then the new table will use InnoDB. In certain cases involving replication, this may cause unintended changes on replicas which use a different engine for the same table. Specifying this option causes the new table to be created with the system's default engine. π
--data-dirβ type: string
Create the new table on a different partition using the DATA DIRECTORY feature. Only available on 5.6+. This parameter is ignored if it's used at the same time as remove-data-dir.
--remove-data-dirβ default: no
If the original table was created using the DATA DIRECTORY feature, remove it and create the new table in the MySQL directory without creating a new isl file.
--remove-tablespaceβ default: no
If the original table was created using the TABLESPACE feature, remove it and create the new table without the TABLESPACE specification.
--defaults-fileβ short form:-F; type: string
Only read mysql options from the given file. You must give an absolute pathname. absolute or nothing bestie π§
--[no]drop-new-tableβ default: yes
Drop the new table if copying the original table fails.
Specifying --no-drop-new-table and --no-swap-tables leaves the new, altered copy of the table without modifying the original table. See --new-table-name.
Heads up:--no-drop-new-tabledoes not work withalter-foreign-keys-method drop_swap. they simply do not vibe together. π
--[no]drop-old-tableβ default: yes
Drop the original table after renaming it. After the original table has been successfully renamed so the new table can take its place, and if there are no errors, the tool drops the original table by default. If there are any errors, the tool leaves the original table in place. safety first, always π«Ά
If --no-swap-tables is specified, then there's no old table to drop.
--[no]drop-triggersβ default: yes
Drop triggers on the old table. --no-drop-triggers forces --no-drop-old-table. they're a package deal, no cap β¨
--dry-run
Create and alter the new table, but do not create triggers, copy data, or replace the original table. a little rehearsal, a little dress rehearsal, nothing actually happens π
--execute
Indicate that you've read the documentation and actually want to alter the table. You must specify this option to alter the table. If you don't, then the tool will only perform some safety checks and exit. This helps ensure that you've read the documentation and understand how to use this tool. If you haven't read the documentation, then do not specify this option. it's the world's simplest vibe check and you WILL respect it π
--[no]check-unique-key-changeβ default: yes
Avoid pt-online-schema-change running if the specified statement for --alter is trying to add an unique index. Since pt-online-schema-change uses INSERT IGNORE to copy rows to the new table, if the row being written produces a duplicate key, it will fail silently and data will be lost. silent data loss is genuinely the scariest thing in this whole manpage π
Example:
CREATE DATABASE test;
USE test;
CREATE TABLE `a` (
`id` int(11) NOT NULL,
`unique_id` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
insert into a values (1, "a");
insert into a values (2, "b");
insert into a values (3, "");
insert into a values (4, "");
insert into a values (5, NULL);
insert into a values (6, NULL);
Using pt-online-schema-change to add an unique index on the unique_id field will cause some rows to be lost due to the use of INSERT IGNORE to copy rows from the source table. For this reason, pt-online-schema-change will fail if it detects that the --alter parameter is trying to add an unique key, and it will show an example query to run to detect if there are rows that will produce duplicated indexes.
Even if you run the query and there are no rows that will produce duplicated indexes, take into consideration that after running this query, changes can be made to the table that can produce duplicate rows and this data will be lost. so like, the check is a snapshot, not a forever-promise π¦
--force
This option bypasses confirmation in case of using alter-foreign-keys-method = none, which might break foreign key constraints.
This option also allows you to use option --where without options --no-drop-new-table and --no-swap-tables.
This option also allows you to bypass the safety check that prevents the tool from running on a replica that's replicating from a source with binary log format ROW or MIXED. bold of you, but okay π₯
--help
Show help and exit.
--history
Write job progress to a table. Unfinished jobs may be restarted by the option --resume. The history table must have this structure (MAGIC_create_pt_osc_history):
CREATE TABLE pt_osc_history (
job_id INT NOT NULL AUTO_INCREMENT,
db CHAR(64) NOT NULL,
tbl CHAR(64) NOT NULL,
new_table_name CHAR(64) DEFAULT NULL,
altr TEXT NOT NULL,
args TEXT NOT NULL,
lower_boundary TEXT,
upper_boundary TEXT,
done ENUM('yes','no') NOT NULL DEFAULT 'no',
ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (job_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Note:lower_boundaryandupper_boundarydata type can beBLOB. See--binary-index. π
--hostβ short form:-h; type: string
Connect to host.
--max-flow-ctlβ type: float
Somewhat similar to --max-lag but for PXC clusters. Check the average time the cluster spent pausing for Flow Control and make the tool pause if it goes over the percentage indicated in the option. A value of 0 would make the tool pause when any Flow Control activity is detected. Default is no Flow Control checking. This option is available for PXC versions 5.6 or higher. π
--max-lagβ type: time; default:1s
Pause the data copy until all replicas' lag is less than this value. After each data-copy query (each chunk), the tool looks at the replication lag of all replicas to which it connects, using Seconds_Behind_Source. If any replica is lagging more than the value of this option, then the tool will sleep for --check-interval seconds, then check all replicas again. If you specify --check-replica-lag, then the tool only examines that server for lag, not all servers. If you want to control exactly which servers the tool monitors, use the DSN value to --recursion-method. π―
The tool waits forever for replicas to stop lagging π If any replica is stopped, it waits forever until that replica is started. The data copy continues when all replicas are running and not lagging too much.
The tool prints progress reports while waiting. If a replica is stopped, it prints a progress report immediately, then again at every progress report interval. Patient queen behavior, honestly. π
--max-loadβ type:Array; default:Threads_running=25. ExamineSHOW GLOBAL STATUSafter every chunk, and pause if any status variables are higher than their thresholds. The option accepts a comma-separated list of MySQL status variables. An optional"=MAX_VALUE"(or":MAX_VALUE") can follow each variable. If not given, the tool determines a threshold by examining the current value and increasing it by 20%. For example, if you want the tool to pause whenThreads_connectedgets too high, you can specifyThreads_connected, and the tool will check the current value when it starts working and add 20% to that value. If the current value is 100, then the tool will pause whenThreads_connectedexceeds 120, and resume working when it is below 120 again. If you want to specify an explicit threshold, such as 110, you can use either"Threads_connected:110"or"Threads_connected=110". The purpose of this option is to prevent the tool from adding too much load to the server. If the data-copy queries are intrusive, or if they cause lock waits, then other queries on the server will tend to block and queue. This will typically causeThreads_runningto increase, and the tool can detect that by runningSHOW GLOBAL STATUSimmediately after each query finishes. If you specify a threshold for this variable, then you can instruct the tool to wait until queries are running normally again. This will not prevent queueing, however; it will only give the server a chance to recover from the queueing. If you notice queueing, it is best to decrease the chunk time. π₯--mysql_sslβ short form:-s; type:int. Create SSL MySQL connection. Secure little vibe. π¦--preserve-triggersβ Preserves old triggers when specified. As of MySQL 5.7.2, it is possible to define multiple triggers for a given table that have the same trigger event and action time. This allows us to add the triggers needed forpt-online-schema-changeeven if the table already has its own triggers. If this option is enabled,pt-online-schema-changewill try to copy all the existing triggers to the new table BEFORE start copying rows from the original table to ensure the old triggers can be applied after altering the table. π
Example.
CREATE TABLE test.t1 (
id INT NOT NULL AUTO_INCREMENT,
f1 INT,
f2 VARCHAR(32),
PRIMARY KEY (id)
);
CREATE TABLE test.log (
ts TIMESTAMP,
msg VARCHAR(255)
);
CREATE TRIGGER test.after_update
AFTER
UPDATE ON test.t1
FOR EACH ROW
INSERT INTO test.log VALUES (NOW(), CONCAT("updated row row with id ", OLD.id, " old f1:", OLD.f1, " new f1: ", NEW.f1 ));
For this table and triggers combination, it is not possible to use --preserve-triggers with an --alter like this: "DROP COLUMN f1" since the trigger references the column being dropped and that would make the trigger fail. π
After testing the triggers will work on the new table, the triggers are dropped from the new table until all rows have been copied and then they are re-applied.
--preserve-triggers cannot be used with these other parameters, --no-drop-triggers, --no-drop-old-table and --no-swap-tables since --preserve-triggers implies that the old triggers should be deleted and recreated in the new table. Since it is not possible to have more than one trigger with the same name, old triggers must be deleted in order to be able to recreate them into the new table.
Using "--preserve-triggers" with "--no-swap-tables" will cause triggers to remain defined for the original table. Please read the documentation for --swap-tables.
If both "--no-swap-tables" and "--no-drop-new-table" is set, the trigger will remain on the original table and will be duplicated on the new table (the trigger will have a random suffix as no trigger names are unique). π
--new-table-nameβ type:string; default:%T_new. New table name before it is swapped.%Tis replaced with the original table name. When the default is used, the tool prefixes the name with up to 10_(underscore) to find a unique table name. If a table name is specified, the tool does not prefix it with_, so the table must not exist. β¨--null-to-not-nullβ AllowsMODIFYing a column that allowsNULLvalues to one that doesn't allow them. The existing rows which containNULLvalues will be converted to the default value based on datatype, e.g.0for number datatypes,''for string datatypes. New rows will use the user defined default value if specified for the column. πΈ--only-same-schema-fksβ Check foreigns keys only on tables on the same schema than the original table. This option is dangerous since if you have FKs referencing tables in other schemas, they won't be detected. π¨
β οΈ Danger, bestie: this one can miss foreign keys in other schemas.
--passwordβ short form:-p; type:string. Password to use when connecting. If password contains commas they must be escaped with a backslash:exam\,ple. π--pause-fileβ type:string. Execution will be paused while the file specified by this param exists. The "touch file to pause" main character. π§--pidβ type:string. Create the given PID file. The tool won't start if the PID file already exists and the PID it contains is different than the current PID. However, if the PID file exists and the PID it contains is no longer running, the tool will overwrite the PID file with the current PID. The PID file is removed automatically when the tool exits. π--pluginβ type:string. Perl module file that defines apt_online_schema_change_pluginclass. A plugin allows you to write a Perl module that can hook into many parts ofpt-online-schema-change. See"PLUGIN"for more information. πͺ--portβ short form:-P; type:int. Port number to use for connection. π--printβ Print SQL statements to STDOUT. Specifying this option allows you to see most of the statements that the tool executes. You can use this option with"--dry-run", for example. π¨οΈ--progressβ type:array; default:time,30. Print progress reports to STDERR while copying rows. The value is a comma-separated list with two parts. The first part can bepercentage,time, oriterations; the second part specifies how often an update should be printed, in percentage, seconds, or number of iterations. π--quietβ short form:-q. Do not print messages to STDOUT (disables"--progress"). Errors and warnings are still printed to STDERR. Shy but still honest. π--recurseβ type:int. Number of levels to recurse in the hierarchy when discovering replicas. Default is infinite. See also"--recursion-method". π--recursion-methodβ type:array; default:processlist,hosts. Preferred recursion method for discovering replicas. Possible methods are:processlistβSHOW PROCESSLISThostsβSHOW REPLICAS(SHOW SLAVE HOSTSbefore MySQL 8.1)dsn=DSNβ DSNs from a tablenoneβ Do not find replicas
The processlist method is the default, because SHOW REPLICAS is not reliable. However, the hosts method can work better if the server uses a non-standard port (not 3306). The tool usually does the right thing and finds all replicas, but you may give a preferred method and it will be used first.
The hosts method requires replicas to be configured with report_host, report_port, etc.
The dsn method is special: it specifies a table from which other DSN strings are read. The specified DSN must specify a D and t, or a database-qualified t. The DSN table should have the following structure:
CREATE TABLE `dsns` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`parent_id` int(11) DEFAULT NULL,
`dsn` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
To make the tool monitor only the hosts 10.10.1.16 and 10.10.1.17 for replication lag, insert the values "h=10.10.1.16" and "h=10.10.1.17" into the table. Currently, the DSNs are ordered by id, but id and parent_id are otherwise ignored.
You can change the list of hosts while OSC is executing: if you change the contents of the DSN table, OSC will pick it up very soon. π
--reverse-triggersβ Copy the triggers added during the copy in reverse order. Commands in the new table will be reflected in the old table. You can use this as a safety feature, so that the old table continues to receive updates. This option requires"--no-drop-old-table".
β οΈ Warning! This option creates reverse triggers on the new table before it starts copying. After new table is renamed to its original name triggers will continue working. But because the name change metadata version in the table cache will also change you may start receiving "Prepared statement needs to be re-prepared" errors. The workaround for this is to re-prepare statements. If you do not use server-side prepared statements your application should not be affected. π
--resumeβ type:int. Resume altering table from the last completed chunk. If the tool stops before it finishes copying rows, this option makes copying resume from the last chunk copied. The option accepts ID of the failed job. This ID is printed whenpt-online-schema-changeis running with option"--history"and stored in the"--history-table".
β οΈ Warning! To use this option previous, failed, run ofpt-online-schema-changeshould use options"--history","--nodrop-new-table", and"--nodrop-triggers". Otherwise,pt-online-schema-changewould not be able to resume. π
--skip-check-replica-lagβ type:DSN; repeatable: yes. That's literally the whole entry here, bestie. π
DSN to skip when checking replica lag. You can drop it multiple times, bestie β it's repeatable. Example:
--skip-check-replica-lag h=127.0.0.1,P=12345 --skip-check-replica-lag h=127.0.0.1,P=12346
Okay but real talk: even though for the MySQL driver h=127.1 equals h=127.0.0.1, for this parameter you gotta spell out the full IP address. No shortcuts. π
--skip-check-slave-lagβ type: DSN; repeatable: yes. Deprecated alert π¨ β this one's getting yeeted in future releases. Use--skip-check-replica-laginstead.--slave-userβ type: string. Also deprecated, also getting removed in future releases. The glow-up replacement is--replica-user. β¨--slave-passwordβ type: string. Deprecated too, same deal, future releases say bye. Use--replica-passwordinstead. π«Ά--replica-userβ type: string. Sets the user the tool uses to connect to the replicas. This is the vibe where you give it a different user with less privileges on the replicas β but like, that user has to exist on all the replicas. No faking it. π--replica-passwordβ type: string. Sets the password used to connect to the replicas. Pairs with--replica-user, and ngl the password for that user must be the same on every single replica.--set-varsβ type: Array. Sets MySQL variables from this comma-separated list ofvariable=valuepairs.
By default the tool sets:
wait_timeout=10000
innodb_lock_wait_timeout=1
lock_wait_timeout=60
Whatever you specify on the command line overrides these defaults, periodt. Like, specifying --set-vars wait_timeout=500 overrides the default value of 10000. π
If a variable can't be set, the tool prints a warning and keeps it moving.
Heads up: setting the sql_mode variable needs some genuinely tricky escapes so the quotes and commas actually parse. Example:
--set-vars sql_mode=\'STRICT_ALL_TABLES\\,ALLOW_INVALID_DATES\'
Note the single backslash for the quotes and double backslash for the comma. Yes, it's giving escape-sequence chaos, but that's the assignment. π
--sleepβ type: float; default:0. How long to sleep (in seconds) after copying each chunk. This is the move when throttling by--max-lagand--max-loadjust isn't possible. Use a small, sub-second value β like0.1β otherwise the tool could take a very long time to copy large tables. π’--socketβ short form:-S; type: string. Socket file to use for the connection. Simple, iconic.--statisticsβ prints statistics about internal counters. So useful for seeing how many warnings were suppressed compared to the number ofINSERT. Detective mode π--[no]swap-tablesβ default:yes. Swaps the original table and the new, altered table. This step is what completes the whole online schema change process β it makes the table with the new schema take the place of the original table. The original becomes the "old table," and the tool drops it unless you disable--[no]drop-old-table. π
Using --no-swap-tables runs the whole process anyway: it creates the new table, copies all the rows, but at the end it drops the new table. It's intended to run a more realistic --dry-run. π
--triesβ type: array. How many times to try critical operations. If certain operations fail due to non-fatal, recoverable errors, the tool waits and tries the operation again. These are the operations that get retried, with their default number of tries and wait time between tries (in seconds):
OPERATION TRIES WAIT
=================== ===== ====
create_triggers 10 1
drop_triggers 10 1
copy_rows 10 0.25
swap_tables 10 1
update_foreign_keys 10 1
analyze_table 10 1
To change the defaults, specify new values like:
--tries create_triggers:5:0.5,drop_triggers:5:0.5
That makes the tool try create_triggers and drop_triggers 5 times with a 0.5 second wait between tries. So the format is:
operation:tries:wait[,operation:tries:wait]
All three values must be specified. No leaving bits out. π
Note that most operations are affected only in MySQL 5.5 and newer by lock_wait_timeout (see --set-vars) because of metadata locks. The copy_rows operation is affected in any version of MySQL by innodb_lock_wait_timeout.
For creating and dropping triggers, the number of tries applies to each CREATE TRIGGER and DROP TRIGGER statement for each trigger. For copying rows, the number of tries applies to each chunk, not the entire table. For swapping tables, the number of tries usually applies once because there's usually only one RENAME TABLE statement. For rebuilding foreign key constraints, the number of tries applies to each statement (ALTER statements for the rebuild_constraints --alter-foreign-keys-method; other statements for the drop_swap method). π§
The tool retries each operation if these errors occur:
Lock wait timeout (innodb_lock_wait_timeout and lock_wait_timeout)
Deadlock found
Query is killed (KILL QUERY <thread_id>)
Connection is killed (KILL CONNECTION <thread_id>)
Lost connection to MySQL
In the case of lost and killed connections, the tool will automatically reconnect. Resilience era. πͺ
Failures and retries are recorded in the --statistics.
--userβ short form:-u; type: string. User for login if not current user.--versionβ shows the version and exits. Short and sweet.--[no]version-checkβ default:yes. Checks for the latest version of Percona Toolkit, MySQL, and other programs. π
It's your standard "check for updates automatically" feature, with two bonus features. First, the tool checks its own version and also the versions of the following software: operating system, Percona Monitoring and Management (PMM), MySQL, Perl, MySQL driver for Perl (DBD::mysql), and Percona Toolkit. Second, it checks for and warns about versions with known problems. For example, MySQL 5.5.25 had a critical bug and was re-released as 5.5.25a. π
A secure connection to Percona's Version Check database server is done to perform these checks. Each request is logged by the server, including software version numbers and unique ID of the checked system. The ID is generated by the Percona Toolkit installation script or when the Version Check database call is done for the first time. π
Any updates or known problems are printed to STDOUT before the tool's normal output. This feature should never interfere with the normal operation of the tool. Pinky promise.
For more information, visit https://www.percona.com/doc/percona-toolkit/LATEST/version-check.html.
--whereβ type: string. Copies only rows matching this WHERE clause. You can use this to limit copying to only part of the table, which is particularly useful if you want to restart apt-online-schema-changejob after it failed. π₯
This option is much like the -w option to mysqldump. Do not specify the WHERE keyword. You might need to quote the value. Here's an example:
pt-online-schema-change --where "id > 12345678"
Warning! If used without options--no-drop-new-tableand--no-swap-tables, this may lead to data loss β therefore this operation is only allowed if option--forceis also specified. Like, actually read that one twice. π
--[no]fail-on-stopped-replicationβ default:yes. If replication is stopped, fail with an error (exit status128) instead of waiting until replication is restarted. β
π§© PLUGIN π§©
okay bestie, so the file you point at with --plugin has to define a class (like, a package, iykyk) called pt_online_schema_change_plugin, and that package needs a new() subroutine. the tool will make an instance of that class and call whatever hooks it defines. technically no hooks are required β but like, a plugin with no hooks is giving "decorative candle", you know? not very useful. π
these hooks get called in this order (if they're defined, that is):
init
before_create_new_table
after_create_new_table
before_alter_new_table
after_alter_new_table
before_create_triggers
after_create_triggers
before_copy_rows
on_copy_rows_after_nibble
after_copy_rows
before_swap_tables
after_swap_tables
before_update_foreign_keys
after_update_foreign_keys
before_drop_old_table
after_drop_old_table
before_drop_triggers
before_die
before_exit
get_slave_lag (deprecated)
get_replica_lag
each hook gets passed different args, bestie β they're not all the same. want to see exactly which args go where? search the hook's name in the tool's source code, like this:
# --plugin hook
if ( $plugin && $plugin->can('init') ) {
$plugin->init(
orig_tbl => $orig_tbl,
child_tables => $child_tables,
renamed_cols => $renamed_cols,
slaves => $slaves,
slave_lag_cxns => $slave_lag_cxns,
);
}
the comment # --plugin hook sits right before every hook call, so it's literally a little treasure map. πΊοΈβ¨
here's a plugin file template with all the hooks in it:
package pt_online_schema_change_plugin;
use strict;
sub new {
my ($class, %args) = @_;
my $self = { %args };
return bless $self, $class;
}
sub init {
my ($self, %args) = @_;
print "PLUGIN init\n";
}
sub before_create_new_table {
my ($self, %args) = @_;
print "PLUGIN before_create_new_table\n";
}
sub after_create_new_table {
my ($self, %args) = @_;
print "PLUGIN after_create_new_table\n";
}
sub before_alter_new_table {
my ($self, %args) = @_;
print "PLUGIN before_alter_new_table\n";
}
sub after_alter_new_table {
my ($self, %args) = @_;
print "PLUGIN after_alter_new_table\n";
}
sub before_create_triggers {
my ($self, %args) = @_;
print "PLUGIN before_create_triggers\n";
}
sub after_create_triggers {
my ($self, %args) = @_;
print "PLUGIN after_create_triggers\n";
}
sub before_copy_rows {
my ($self, %args) = @_;
print "PLUGIN before_copy_rows\n";
}
sub on_copy_rows_after_nibble {
my ($self, %args) = @_;
print "PLUGIN on_copy_rows_after_nibble\n";
}
sub after_copy_rows {
my ($self, %args) = @_;
print "PLUGIN after_copy_rows\n";
}
sub before_swap_tables {
my ($self, %args) = @_;
print "PLUGIN before_swap_tables\n";
}
sub after_swap_tables {
my ($self, %args) = @_;
print "PLUGIN after_swap_tables\n";
}
sub before_update_foreign_keys {
my ($self, %args) = @_;
print "PLUGIN before_update_foreign_keys\n";
}
sub after_update_foreign_keys {
my ($self, %args) = @_;
print "PLUGIN after_update_foreign_keys\n";
}
sub before_drop_old_table {
my ($self, %args) = @_;
print "PLUGIN before_drop_old_table\n";
}
sub after_drop_old_table {
my ($self, %args) = @_;
print "PLUGIN after_drop_old_table\n";
}
sub before_drop_triggers {
my ($self, %args) = @_;
print "PLUGIN before_drop_triggers\n";
}
sub before_die {
my ($self, %args) = @_;
print "PLUGIN before_die\n";
}
sub before_exit {
my ($self, %args) = @_;
print "PLUGIN before_exit\n";
}
sub get_replica_lag {
my ($self, %args) = @_;
print "PLUGIN get_replica_lag\n";
return sub { return 0; };
}
sub get_slave_lag {
my ($self, %args) = @_;
print "PLUGIN get_slave_lag\n";
return sub { return 0; };
}
1;
β οΈ heads up: get_replica_lag must return a function reference β and ideally one that returns actual replica lag, not just a lazy zero like the example does. no cap, don't ship the zero unless you truly don't care. π
and here's one that actually does something real:
package pt_online_schema_change_plugin;
use strict;
sub new {
my ($class, %args) = @_;
my $self = { %args };
return bless $self, $class;
}
sub after_create_new_table {
my ($self, %args) = @_;
my $new_tbl = $args{new_tbl};
my $dbh = $self->{cxn}->dbh;
my $row = $dbh->selectrow_arrayref("SHOW CREATE TABLE $new_tbl->{name}");
warn "after_create_new_table: $row->[1]\n\n";
}
sub after_alter_new_table {
my ($self, %args) = @_;
my $new_tbl = $args{new_tbl};
my $dbh = $self->{cxn}->dbh;
my $row = $dbh->selectrow_arrayref("SHOW CREATE TABLE $new_tbl->{name}");
warn "after_alter_new_table: $row->[1]\n\n";
}
1;
you can pair that with --dry-run to peep how the table's gonna look before and after. lowkey the best way to vibe-check your schema change before it goes live. π
questions? need help? hit up Percona. they're the ones who actually know. π«Ά
π DSN OPTIONS π
these DSN options are what you use to build a DSN. every option goes in like option=value. they're case-sensitive, so P and p are not the same option, bestie β do not mix them up. there can't be any whitespace before or after the =, and if your value has whitespace in it, it's gotta be quoted. DSN options are comma-separated. full deets live in the percona-toolkit manpage. β¨
Aβ dsn:charset; copy: yes. this is the default character set.Dβ dsn:database; copy: no. the database for the old and the new table.Fβ dsn:mysql_read_default_file; copy: yes. only read default options from the file you give it.hβ dsn:host; copy: yes. connect to this host.pβ dsn:password; copy: yes. the password to use when connecting. if your password has commas in it you must escape them with a backslash, like:exam\,ple. (yes really, escape the commas. π)Pβ dsn:port; copy: yes. port number to use for the connection.Sβ dsn:mysql_socket; copy: yes. the socket file to use for the connection.tβ dsn:table; copy: no. the table to alter.uβ dsn:user; copy: yes. user for login, if it's not the current user.sβ dsn:mysql_ssl; copy: yes. create an SSL connection.
π± ENVIRONMENT π±
the environment variable PTDEBUG turns on verbose debugging output to STDERR. want debugging and everything captured into a file? run the tool like this:
PTDEBUG=1 pt-online-schema-change ... > FILE 2>&1
β οΈ be careful, bestie: debugging output is voluminous and can generate several megabytes of output. like, megabytes. plural. π«
π¨ ATTENTION π¨
using <PTDEBUG> might expose passwords. when debug is enabled, all command line parameters are shown in the output. so yeah β maybe don't paste that debug dump into the group chat. ππ
πͺ EXIT STATUS πͺ
INVALID_PARAMETERS = 1
UNSUPPORTED_MYSQL_VERSION = 2
NO_MINIMUM_REQUIREMENTS = 3
NO_PRIMARY_OR_UNIQUE_KEY = 4
INVALID_PLUGIN_FILE = 5
INVALID_ALTER_FK_METHOD = 6
INVALID_KEY_SIZE = 7
CANNOT_DETERMINE_KEY_SIZE = 9
NOT_SAFE_TO_ASCEND = 9
ERROR_CREATING_NEW_TABLE = 10
ERROR_ALTERING_TABLE = 11
ERROR_CREATING_TRIGGERS = 12
ERROR_RESTORING_TRIGGERS = 13
ERROR_SWAPPING_TABLES = 14
ERROR_UPDATING_FKS = 15
ERROR_DROPPING_OLD_TABLE = 16
UNSUPPORTED_OPERATION = 17
MYSQL_CONNECTION_ERROR = 18
LOST_MYSQL_CONNECTION = 19
ERROR_CREATING_REVERSE_TRIGGERS = 20
(yes, 9 shows up twice β CANNOT_DETERMINE_KEY_SIZE and NOT_SAFE_TO_ASCEND are both 9. that's just how it is. π
)
π SYSTEM REQUIREMENTS π
you need Perl, DBI, DBD::mysql, and some core packages that ought to be installed in any reasonably new version of Perl. like, any self-respecting modern Perl install has 'em. πͺ
this tool works only on MySQL 5.0.2 and newer β because earlier versions don't support triggers, full stop. also, a whole bunch of permissions should be set on MySQL so pt-online-schema-change actually works the way you expect. grant these global privileges: PROCESS, SUPER, REPLICATION SLAVE. and on top of that, these table privileges: SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, and TRIGGER. the replica only needs REPLICATION SLAVE and REPLICATION CLIENT privileges. π
π BUGS π
for a list of known bugs, go look at <https://jira.percona.com/projects/PT/issues>.
please report bugs at <https://jira.percona.com/projects/PT>. and when you do, slide these into the report so people can actually help you:
- the complete command-line you used to run the tool
- the tool's
--version - the MySQL version of all servers involved
- the output from the tool, including STDERR
- your input files (log/dump/config files, whatever applies)
if you can, also include debugging output by running the tool with PTDEBUG β see "ENVIRONMENT" above. π
β¬οΈ DOWNLOADING β¬οΈ
visit <http://www.percona.com/software/percona-toolkit/> to grab the latest release of Percona Toolkit. or, honestly, just grab it straight from the command line like a main character:
wget percona.com/get/percona-toolkit.tar.gz
wget percona.com/get/percona-toolkit.rpm
wget percona.com/get/percona-toolkit.deb
you can also snag individual tools from the latest release:
wget percona.com/get/TOOL
just swap TOOL for the name of whatever tool you're after. β¨
π AUTHORS π
Daniel Nichter and Baron Schwartz
π«‘ ACKNOWLEDGMENTS π«‘
the whole "online schema change" concept was first implemented by Shlomi Noach in his tool oak-online-alter-table, which is part of <http://code.google.com/p/openarkkit/>. then the engineers at Facebook built another version called OnlineSchemaChange.php, as explained in their blog post: <http://tinyurl.com/32zeb86>. this tool is a hybrid of both approaches β with extra features and functionality that neither one has. iconic lineage honestly. π
π ABOUT PERCONA TOOLKIT π
this tool is part of Percona Toolkit, a collection of advanced command-line tools for MySQL developed by Percona. Percona Toolkit was forked from two projects back in June, 2011: Maatkit and Aspersa. those projects were created by Baron Schwartz and primarily developed by him and Daniel Nichter. visit <http://www.percona.com/software/> to learn about other free, open-source software from Percona. π¦
π COPYRIGHT, LICENSE, AND WARRANTY π
Okay bestie, legal era, but make it cute β this part actually matters so don't scroll, iykyk.
This program is copyright 2011-2026 Percona LLC and/or its affiliates. β¨
π«Ά The warranty part (there isn't one, fr)
THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Like, no cap β zero warranty. If it breaks, it breaks. That's the deal. π
π The license part (slay, it's free)
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2; OR the Perl Artistic License. Pick your fighter, both are valid. π
On UNIX and similar systems, you can literally just run:
man perlgpl
or
man perlartistic
to read these licenses. Iconic behavior, honestly. π
You should have received a copy of the GNU General Public License, version 2 along with this program; if not, see:
https://www.gnu.org/licenses/
(That's the whole URL exactly, don't @ me.) π
β VERSION β
pt-online-schema-change 3.7.1-4
That's the one. Write it down, screenshot it, tattoo it, whatever. π
π POD ERRORS π
Not me having to tell you the docs themselves had a lil moment. π Here's the tea:
Hey! The above document had some coding errors, which are explained below:
- Around line 13863: Non-ASCII character seen before
=encodingin'Perconaβs'. AssumingUTF-8
Yeah so the curly apostrophe in Perconaβs showed up before the =encoding line told Perl what was up, and it just went "fine, UTF-8 then, whatever." Lowkey a vibe check failure but highkey harmless. π
π¦ That's a wrap, bestie π¦
You made it to the end of the whole page β genuinely obsessed with your commitment. π«Ά Go break absolutely nothing, or at least take a backup first. π
Signing off from perl v5.42.2, dated 2026-09-01, and filed under PT-ONLI...A-CHANGE(1) β yes the name is truncated, no we don't know why, it's giving main character energy anyway. β¨ππ
π¦ where this page comes from
- arch rolling: percona-toolkit 3.7.1_4-1 Β· GPL
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms