diff --git a/doc/user/content/ingest-data/mysql/source-versioning.md b/doc/user/content/ingest-data/mysql/source-versioning.md deleted file mode 100644 index 05edc044d5ce2..0000000000000 --- a/doc/user/content/ingest-data/mysql/source-versioning.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: "Guide: Handle upstream schema changes with zero downtime" -description: "How to add a column, or drop a column, from your source MySQL database, without any downtime in Materialize" - -menu: - main: - parent: "mysql" - identifier: "mysql-source-versioning" - weight: 85 ---- - -{{< private-preview />}} -{{< note >}} -Changing column types is currently unsupported. -{{< /note >}} - -Materialize allows you to handle certain types of upstream -table schema changes seamlessly, specifically: - -- Adding a column in the upstream database. -- Dropping a column in the upstream database. - -This guide walks you through how to handle these changes without any downtime in Materialize. - -## Prerequisites - -Some familiarity with Materialize. If you've never used Materialize before, -start with our [guide to getting started](/get-started/quickstart/) to learn -how to connect a database to Materialize. - -### Set up a MySQL database - -For this guide, setup a MySQL 5.7+ database. In your MySQL database, create a -table `t1` and populate it: - -```sql -CREATE TABLE t1 ( - a INT -); - -INSERT INTO t1 (a) VALUES (10); -``` - -### Configure your MySQL database - -Configure your MySQL database for GTID-based binlog replication using the -[configuration instructions for self-hosted MySQL](/ingest-data/mysql/self-hosted/#a-configure-mysql). - -### Connect your source database to Materialize - -Create a connection to your MySQL database using the [`CREATE CONNECTION` syntax](/sql/create-connection/). - -## Create a source - -In Materialize, create a source using the [`CREATE SOURCE` -syntax](/sql/create-source/mysql/). - -```mzsql -CREATE SOURCE my_source - FROM MYSQL CONNECTION mysql_connection; -``` - -## Create a table from the source - -To start ingesting specific tables from your source database, create a -table in Materialize. We'll add it into the `v1` schema. - -```mzsql -CREATE SCHEMA v1; - -CREATE TABLE v1.t1 - FROM SOURCE my_source (REFERENCE mydb.t1); -``` - -Once you've created a table from source, the [initial -snapshot](/ingest-data/#snapshotting) of table `v1.t1` will begin. - -{{< note >}} - -During the snapshotting, the data ingestion for the other tables associated with -the source is temporarily blocked. You can monitor progress for the snapshot -operation on the overview page for the source in the Materialize console. - -{{< /note >}} - -## Create a view on top of the table - -For this guide, add a materialized view `matview` (also in schema `v1`) that -sums column `a` from table `t1`. - -```mzsql -CREATE MATERIALIZED VIEW v1.matview AS - SELECT SUM(a) FROM v1.t1; -``` - -## Handle upstream column addition - -### A. Add a column in your upstream MySQL database - -In your upstream MySQL database, add a new column `b` to the table `t1`: - -```sql -ALTER TABLE t1 - ADD COLUMN b BOOLEAN DEFAULT false; - -INSERT INTO t1 (a, b) VALUES (20, true); -``` - -This operation has no immediate effect in Materialize. In Materialize: - -- The table `v1.t1` will continue to ingest only column `a`. -- The materialized view `v1.matview` will continue to have access to column `a` - only. - -### B. Incorporate the new column in Materialize - -Unlike SQL Server CDC, MySQL uses binlog-based replication, which automatically -includes all columns. To incorporate the new column into Materialize, create a -new `v2` schema and recreate the table in the new schema: - -```mzsql -CREATE SCHEMA v2; - -CREATE TABLE v2.t1 - FROM SOURCE my_source (REFERENCE mydb.t1); -``` - -The [snapshotting](/ingest-data/#snapshotting) of table `v2.t1` will begin. -`v2.t1` will include columns `a` and `b`. - -{{< note >}} - -During the snapshotting, the data ingestion for the other tables associated with -the source is temporarily blocked. You can monitor progress for the snapshot -operation on the overview page for the source in the Materialize console. - -{{< /note >}} - -When `v2.t1` has finished snapshotting, create a new materialized view in the -new schema. Since `v2.matview` references `v2.t1`, it can now reference column `b`: - -```mzsql {hl_lines="4"} -CREATE MATERIALIZED VIEW v2.matview AS - SELECT SUM(a) - FROM v2.t1 - WHERE b = true; -``` - -## Handle upstream column drop - -### A. Exclude the column in Materialize - -To drop a column safely, first create a new schema in Materialize and recreate -the table excluding the column you intend to drop. In this example, we'll drop -column `b`. - -```mzsql -CREATE SCHEMA v3; - -CREATE TABLE v3.t1 - FROM SOURCE my_source (REFERENCE mydb.t1) WITH (EXCLUDE COLUMNS (b)); -``` - -{{< note >}} - -During the snapshotting, the data ingestion for the other tables associated with -the source is temporarily blocked. You can monitor progress for the snapshot -operation on the overview page for the source in the Materialize console. - -{{< /note >}} - -### B. Drop the column in your upstream MySQL database - -In your upstream MySQL database, drop column `b` from table `t1`: - -```sql -ALTER TABLE t1 DROP COLUMN b; -``` - -Dropping column `b` will have no effect on `v3.t1` in Materialize, provided -you completed step A before dropping the column. However, the drop affects -`v2.T` and `v2.matview` from our earlier examples. When the user attempts to -read from either, Materialize will report an error that the source table schema -has been altered. - -Once you have finished migrating any views and queries from `v2` to `v3`, you -can clean up the old objects: - -```mzsql -DROP TABLE v2.t1; -DROP MATERIALIZED VIEW v2.matview; -DROP SCHEMA v2; -``` diff --git a/doc/user/data/mysql_config_settings.yml b/doc/user/data/mysql_config_settings.yml index 5939c72eda263..76f38b3df6ada 100644 --- a/doc/user/data/mysql_config_settings.yml +++ b/doc/user/data/mysql_config_settings.yml @@ -15,10 +15,6 @@ rows: Value: "`FULL`" Notes: "" - - MySQL Configuration: "`binlog_row_metadata`" - Value: "`FULL`" - Notes: "Required when using the `CREATE TABLE FROM SOURCE` syntax." - - MySQL Configuration: "`gtid_mode`" Value: "`ON`" Notes: "{{ $gtid_mode_note }}" diff --git a/src/mysql-util/src/decoding.rs b/src/mysql-util/src/decoding.rs index f0ff20f3d270b..18aa6e101600a 100644 --- a/src/mysql-util/src/decoding.rs +++ b/src/mysql-util/src/decoding.rs @@ -30,44 +30,9 @@ pub fn pack_mysql_row( table_desc: &MySqlTableDesc, ) -> Result { let mut packer = row_container.packer(); + let row_values = row.unwrap(); - // If a column name begins with '@', then the binlog does not have full row metadata, - // meaning that full column names are not available and we need to rely on the order - // of the columns in the upstream table matching the order of the columns in the row. - // This is a fallback for MySQL servers that do not have `binlog_row_metadata` set to - // `FULL`. If the first column name does not begin with '@', then we can assume that - // full metadata is available and we can match columns by name. - let row_values: Vec = if row - .columns_ref() - .first() - .is_some_and(|col| col.name_ref().starts_with(b"@")) - { - row.unwrap() - } else { - row.columns_ref() - .iter() - .enumerate() - .filter(|(_, col)| { - table_desc - .columns - .iter() - .filter(|col| col.column_type.is_some()) - .any(|c| c.name.as_str() == col.name_str()) - }) - .map(|(i, _)| { - row.as_ref(i) - .expect("Can't unwrap row if some of columns was taken") - .clone() - }) - .collect() - }; - - for values in table_desc - .columns - .iter() - .filter(|col| col.column_type.is_some()) - .zip_longest(row_values) - { + for values in table_desc.columns.iter().zip_longest(row_values) { let (col_desc, value) = match values { EitherOrBoth::Both(col_desc, value) => (col_desc, value), EitherOrBoth::Left(col_desc) => { diff --git a/src/mysql-util/src/desc.rs b/src/mysql-util/src/desc.rs index 73e16462365d9..8464dff2b4273 100644 --- a/src/mysql-util/src/desc.rs +++ b/src/mysql-util/src/desc.rs @@ -75,11 +75,7 @@ impl MySqlTableDesc { /// exceptions: /// - `self`'s columns are a prefix of `other`'s columns. /// - `self`'s keys are all present in `other` - pub fn determine_compatibility( - &self, - other: &MySqlTableDesc, - full_metadata: bool, - ) -> Result<(), anyhow::Error> { + pub fn determine_compatibility(&self, other: &MySqlTableDesc) -> Result<(), anyhow::Error> { if self == other { return Ok(()); } @@ -94,34 +90,18 @@ impl MySqlTableDesc { ); } - // In the case that we don't have full binlog row metadata, `columns` is ordered by the - // ordinal_position of each column in the table, so as long as `self.columns` is a - // compatible prefix of `other.columns`, we can ignore extra columns from `other.columns`. - // - // If we do have full metadata, then we can match columns by name and just check that all - // columns in `self.columns` are present and compatible with columns in `other.columns`. + // `columns` is ordered by the ordinal_position of each column in the table, + // so as long as `self.columns` is a compatible prefix of `other.columns`, we can + // ignore extra columns from `other.columns`. let mut other_columns = other.columns.iter(); for self_column in &self.columns { - let other_column = if full_metadata { - other_columns - .by_ref() - .find(|c| c.name == self_column.name) - .ok_or_else(|| { - anyhow::anyhow!( - "column {} no longer present in table {}", - self_column.name, - self.name - ) - })? - } else { - other_columns.next().ok_or_else(|| { - anyhow::anyhow!( - "column {} no longer present in table {}", - self_column.name, - self.name - ) - })? - }; + let other_column = other_columns.next().ok_or_else(|| { + anyhow::anyhow!( + "column {} no longer present in table {}", + self_column.name, + self.name + ) + })?; if !self_column.is_compatible(other_column) { bail!( "column {} in table {} has been altered", @@ -130,6 +110,7 @@ impl MySqlTableDesc { ); } } + // Our keys are all still present in exactly the same shape. // TODO: Implement a more relaxed key compatibility check: // We should check that for all keys that we know about there exists an upstream key whose diff --git a/src/sql/src/pure.rs b/src/sql/src/pure.rs index de8a885468ed1..43ddba044e008 100644 --- a/src/sql/src/pure.rs +++ b/src/sql/src/pure.rs @@ -1858,16 +1858,6 @@ async fn purify_create_table_from_source( ) .await?; - let binlog_metadata_setting = - mz_mysql_util::query_sys_var(&mut conn, "binlog_row_metadata").await?; - if binlog_metadata_setting != "FULL" { - Err( - MySqlSourcePurificationError::UnsupportedBinlogMetadataSetting { - setting: binlog_metadata_setting, - }, - )?; - } - // Retrieve the current @gtid_executed value of the server to mark as the effective // initial snapshot point for this table. let initial_gtid_set = diff --git a/src/sql/src/pure/error.rs b/src/sql/src/pure/error.rs index 2e7c4be77efaf..418a51c5d955d 100644 --- a/src/sql/src/pure/error.rs +++ b/src/sql/src/pure/error.rs @@ -310,10 +310,6 @@ pub enum MySqlSourcePurificationError { NoTablesFoundForSchemas(Vec), #[error(transparent)] InvalidConnection(#[from] MySqlConnectionValidationError), - #[error( - "The MySQL system variable 'binlog_row_metadata' is set to an unsupported value: {setting}. Materialize requires this variable to be set to 'FULL' to use the \"CREATE TABLE FROM SOURCE\" syntax for MySQL sources." - )] - UnsupportedBinlogMetadataSetting { setting: String }, } impl MySqlSourcePurificationError { diff --git a/src/storage/src/source/mysql/schemas.rs b/src/storage/src/source/mysql/schemas.rs index 379e8dc167e0b..55bc91fcb0487 100644 --- a/src/storage/src/source/mysql/schemas.rs +++ b/src/storage/src/source/mysql/schemas.rs @@ -45,13 +45,6 @@ where }) .collect(); - let full_metadata = conn - .query_first::("SELECT @@binlog_row_metadata".to_string()) - .await? - .unwrap() - .to_uppercase() - == "FULL"; - Ok(expected .into_iter() .flat_map(|(table, outputs)| { @@ -71,15 +64,13 @@ where )), ); match new_desc { - Ok(desc) => { - match output.desc.determine_compatibility(&desc, full_metadata) { - Ok(()) => None, - Err(err) => Some(( - output, - DefiniteError::IncompatibleSchema(err.to_string()), - )), - } - } + Ok(desc) => match output.desc.determine_compatibility(&desc) { + Ok(()) => None, + Err(err) => Some(( + output, + DefiniteError::IncompatibleSchema(err.to_string()), + )), + }, Err(err) => { Some((output, DefiniteError::IncompatibleSchema(err.to_string()))) } diff --git a/test/mysql-cdc-old-syntax/mzcompose.py b/test/mysql-cdc-old-syntax/mzcompose.py index 363ee1d32588e..7397185354c0c 100644 --- a/test/mysql-cdc-old-syntax/mzcompose.py +++ b/test/mysql-cdc-old-syntax/mzcompose.py @@ -41,9 +41,7 @@ def create_mysql(mysql_version: str) -> MySql: - return MySql( - version=mysql_version, additional_args=["--binlog_row_metadata=MINIMAL"] - ) + return MySql(version=mysql_version) def create_mysql_replica(mysql_version: str) -> MySql: @@ -55,7 +53,6 @@ def create_mysql_replica(mysql_version: str) -> MySql: "--enforce_gtid_consistency=ON", "--skip-replica-start", "--server-id=2", - "--binlog_row_metadata=MINIMAL", ], ) diff --git a/test/mysql-cdc-resumption/mzcompose.py b/test/mysql-cdc-resumption/mzcompose.py index f5ddf2e6d788c..58819d32cef31 100644 --- a/test/mysql-cdc-resumption/mzcompose.py +++ b/test/mysql-cdc-resumption/mzcompose.py @@ -615,7 +615,7 @@ def backup_restore_mysql(c: Composition) -> None: # TODO: database-issues#7683: one of the two following commands must succeed # run_testdrive_files(c, "verify-rows-after-restore-t1.td") - # run_testdrive_files(c, "verify-source-failed.td") + run_testdrive_files(c, "verify-source-failed.td") def create_source_after_logs_expiration( diff --git a/test/mysql-cdc/alter-column-irrelevant.td b/test/mysql-cdc/alter-column-irrelevant.td index 4a2caff1f6bac..d785efb657d93 100644 --- a/test/mysql-cdc/alter-column-irrelevant.td +++ b/test/mysql-cdc/alter-column-irrelevant.td @@ -59,12 +59,10 @@ INSERT INTO t1 VALUES (2, 2); # add a new column to t1 at the beginning of $ mysql-execute name=mysql ALTER TABLE t1 ADD COLUMN f3 INTEGER FIRST; -INSERT INTO t1 VALUES (0, 3, 3); +INSERT INTO t1 VALUES (3, 3, 3); -> SELECT * FROM t1; -1 -2 -3 +! SELECT * FROM t1; +contains:incompatible schema change # add a new column to t2 $ mysql-execute name=mysql diff --git a/test/mysql-cdc/binlog-backward-compat.td b/test/mysql-cdc/binlog-backward-compat.td deleted file mode 100644 index 04115e15bfc6d..0000000000000 --- a/test/mysql-cdc/binlog-backward-compat.td +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright Materialize, Inc. and contributors. All rights reserved. -# -# Use of this software is governed by the Business Source License -# included in the LICENSE file at the root of this repository. -# -# As of the Change Date specified in that file, in accordance with -# the Business Source License, use of this software will be governed -# by the Apache License, Version 2.0. - -# Verify that MySQL replication works correctly when binlog_row_metadata is -# set to MINIMAL (the MySQL default prior to 8.0). In MINIMAL mode the binlog -# does not include column names, so Materialize must fall back to matching -# columns by position rather than by name. - -> CREATE SECRET mysqlpass AS '${arg.mysql-root-password}' - -> CREATE CONNECTION mysql_conn TO MYSQL ( - HOST mysql, - USER root, - PASSWORD SECRET mysqlpass - ) - -$ mysql-connect name=mysql url=mysql://root@mysql password=${arg.mysql-root-password} - -$ mysql-execute name=mysql -SET GLOBAL binlog_row_metadata = MINIMAL; -DROP DATABASE IF EXISTS public; -CREATE DATABASE public; -USE public; -CREATE TABLE foo (name VARCHAR(16), value VARCHAR(32)); -INSERT INTO foo VALUES ('a', 'apple'), ('b', 'banana'); - -> CREATE SOURCE mysql_src FROM MYSQL CONNECTION mysql_conn FOR TABLES (foo); - -> SELECT * FROM foo; -a apple -b banana - -$ mysql-execute name=mysql -INSERT INTO foo VALUES ('c', 'cherry'); - -> SELECT * FROM foo; -a apple -b banana -c cherry - -$ mysql-execute name=mysql -UPDATE foo SET value = 'avocado' WHERE name = 'a'; - -> SELECT * FROM foo; -a avocado -b banana -c cherry - -$ mysql-execute name=mysql -DELETE FROM foo WHERE name = 'b'; - -> SELECT * FROM foo; -a avocado -c cherry - -> DROP SOURCE mysql_src CASCADE; - -# Restore to a clean state so other tests are unaffected. -$ mysql-execute name=mysql -SET GLOBAL binlog_row_metadata = FULL; diff --git a/test/mysql-cdc/binlog-row-metadata-check.td b/test/mysql-cdc/binlog-row-metadata-check.td deleted file mode 100644 index a25d55b63b526..0000000000000 --- a/test/mysql-cdc/binlog-row-metadata-check.td +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright Materialize, Inc. and contributors. All rights reserved. -# -# Use of this software is governed by the Business Source License -# included in the LICENSE file at the root of this repository. -# -# As of the Change Date specified in that file, in accordance with -# the Business Source License, use of this software will be governed -# by the Apache License, Version 2.0. - -# Test that CREATE TABLE FROM SOURCE fails when binlog_row_metadata is not FULL. - -$ mysql-connect name=mysql url=mysql://root@mysql password=${arg.mysql-root-password} - -$ mysql-execute name=mysql -SET GLOBAL binlog_row_metadata = MINIMAL; -DROP DATABASE IF EXISTS public; -CREATE DATABASE public; -USE public; -CREATE TABLE t1 (id INT PRIMARY KEY, val TEXT); -INSERT INTO t1 VALUES (1, 'hello'); - -> CREATE SECRET mysqlpass AS '${arg.mysql-root-password}' - -> CREATE CONNECTION mysql_conn TO MYSQL ( - HOST mysql, - USER root, - PASSWORD SECRET mysqlpass - ) - -> CREATE SOURCE mz_source FROM MYSQL CONNECTION mysql_conn; - -! CREATE TABLE t1 FROM SOURCE mz_source (REFERENCE public.t1); -contains: binlog_row_metadata - -# Restore to a clean state so other tests are unaffected. -$ mysql-execute name=mysql -SET GLOBAL binlog_row_metadata = FULL; diff --git a/test/mysql-cdc/mzcompose.py b/test/mysql-cdc/mzcompose.py index e4b9ebaf51690..c6837f3ad88ba 100644 --- a/test/mysql-cdc/mzcompose.py +++ b/test/mysql-cdc/mzcompose.py @@ -47,7 +47,6 @@ def create_mysql_replica(mysql_version: str) -> MySql: "--enforce_gtid_consistency=ON", "--skip-replica-start", "--server-id=2", - "--binlog_row_metadata=FULL", ], ) diff --git a/test/mysql-cdc/upstream-schema-changes.td b/test/mysql-cdc/upstream-schema-changes.td deleted file mode 100644 index da3ea47c77c2e..0000000000000 --- a/test/mysql-cdc/upstream-schema-changes.td +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright Materialize, Inc. and contributors. All rights reserved. -# -# Use of this software is governed by the Business Source License -# included in the LICENSE file at the root of this repository. -# -# As of the Change Date specified in that file, in accordance with -# the Business Source License, use of this software will be governed -# by the Apache License, Version 2.0. - -# Perform various schema updates to the upstream table - -> CREATE SECRET mysqlpass AS '${arg.mysql-root-password}' - -> CREATE CONNECTION mysql_conn TO MYSQL ( - HOST mysql, - USER root, - PASSWORD SECRET mysqlpass - ) - -$ mysql-connect name=mysql url=mysql://root@mysql password=${arg.mysql-root-password} - -$ mysql-execute name=mysql -DROP DATABASE IF EXISTS public; -CREATE DATABASE public; -USE public; -CREATE TABLE foo (name VARCHAR(16), value VARCHAR(32)); -INSERT INTO foo VALUES ('a', 'apple'), ('b', 'banana'); - -> CREATE SOURCE mysql_src FROM MYSQL CONNECTION mysql_conn; -> CREATE TABLE foo1 FROM SOURCE mysql_src (REFERENCE public.foo); - -> SELECT * FROM foo1; -a apple -b banana - -$ mysql-execute name=mysql -ALTER TABLE foo ADD COLUMN meta_col VARCHAR(32); -INSERT INTO foo VALUES ('c', 'cherry', 'wild'); - -# Adding a column to the upstream table does not affect foo1 — it continues to -# replicate only the columns it was created with, ignoring the new meta_col. -> SELECT * FROM foo1; -a apple -b banana -c cherry - -$ mysql-execute name=mysql -ALTER TABLE foo MODIFY meta_col VARCHAR(64); -INSERT INTO foo VALUES ('d', 'date', 'ajwa'); - -# Altering the newly added `meta_col` column does not brick `foo1` because `foo1` is -# not following/replicating `meta_col`, so schema updates involving it are inconsequential. -> SELECT * FROM foo1; -a apple -b banana -c cherry -d date - -> DROP TABLE foo1; - -# Unlike SQL Server CDC, MySQL binlog-based replication does not require a new capture -# instance for the new column. Creating a table from the existing source will include -# meta_col from the snapshot onward. -> CREATE TABLE foo2 FROM SOURCE mysql_src (REFERENCE public.foo); -> SELECT * FROM foo2; -a apple -b banana -c cherry wild -d date ajwa - -$ mysql-execute name=mysql -INSERT INTO foo VALUES ('e', 'elderberry', 'montypython'); - -> SELECT * FROM foo2; -a apple -b banana -c cherry wild -d date ajwa -e elderberry montypython - -> DROP TABLE foo2; - -# We can also use EXCLUDE COLUMNS to exclude meta_col if desired. -> CREATE TABLE foo3 FROM SOURCE mysql_src (REFERENCE public.foo) WITH (EXCLUDE COLUMNS = (meta_col)); -> SELECT * FROM foo3; -a apple -b banana -c cherry -d date -e elderberry - -$ mysql-execute name=mysql -INSERT INTO foo VALUES ('f', 'fig', 'sweet'); - -> SELECT * FROM foo3; -a apple -b banana -c cherry -d date -e elderberry -f fig - -# dropping an excluded column should have no effect -$ mysql-execute name=mysql -ALTER TABLE foo DROP COLUMN meta_col; - -> SELECT * FROM foo3; -a apple -b banana -c cherry -d date -e elderberry -f fig - -> DROP TABLE foo3; - -# After meta_col has been dropped upstream, foo4 can be created without excluding it. -> CREATE TABLE foo4 FROM SOURCE mysql_src (REFERENCE public.foo); -> SELECT * FROM foo4; -a apple -b banana -c cherry -d date -e elderberry -f fig - -$ mysql-execute name=mysql -INSERT INTO foo VALUES ('g', 'grape'); - -> SELECT * FROM foo4; -a apple -b banana -c cherry -d date -e elderberry -f fig -g grape - -# Dropping a non-excluded (tracked) column stalls the source. -$ mysql-execute name=mysql -ALTER TABLE foo DROP COLUMN value; - -! SELECT * FROM foo4; -contains:incompatible schema change - -> DROP TABLE foo4; - -# Test with multiple excluded columns. -$ mysql-execute name=mysql -CREATE TABLE bar (name VARCHAR(16), value VARCHAR(32), age BIGINT, location VARCHAR(32), meta_col VARCHAR(32)); -INSERT INTO bar VALUES ('a', 'apple', 5, 'orchard', 'blah'), ('b', 'banana', 7, 'tree', 'blahblah'); - -> CREATE TABLE bar1 FROM SOURCE mysql_src (REFERENCE public.bar) WITH (EXCLUDE COLUMNS = (meta_col, location)); - -> SELECT * FROM bar1; -a apple 5 -b banana 7 - -$ mysql-execute name=mysql -ALTER TABLE bar DROP COLUMN meta_col; - -> SELECT * FROM bar1; -a apple 5 -b banana 7 - -$ mysql-execute name=mysql -ALTER TABLE bar ADD COLUMN lastname VARCHAR(16) AFTER `name`; - -> SELECT * FROM bar1; -a apple 5 -b banana 7 - -> DROP SOURCE mysql_src CASCADE;