Database transfer reference

Examples use $URL, $STATE_DIR, $FS_ROOT, and an authorized ./reprint-key.pem as described in Conventions. Run preflight with the same source address and state directory before downloading data.

Download SQL

By default, this streams a SQL dump into $STATE_DIR/db.sql:

php reprint.phar db-pull "$URL" \
  --private-key-path=./reprint-key.pem --state-dir="$STATE_DIR" --fs-root="$FS_ROOT"

You can also pipe the SQL directly to stdout or stream it into a MySQL server without writing a file to disk. Use --sql-output to choose the mode:

# Pipe to stdout — useful for feeding into mysql CLI or another tool
php reprint.phar db-pull "$URL" \
  --private-key-path=./reprint-key.pem --state-dir="$STATE_DIR" --fs-root="$FS_ROOT" \
    --sql-output=stdout | mysql --binary-mode --local-infile=0 -u import_user -p my_database

# Stream directly into MySQL — no intermediate file, no pipe
php reprint.phar db-pull "$URL" \
  --private-key-path=./reprint-key.pem --state-dir="$STATE_DIR" --fs-root="$FS_ROOT" \
    --sql-output=mysql --mysql-database=my_database --mysql-host=127.0.0.1 --mysql-user=import_user --mysql-password=secret

For a pipe into the mysql CLI, keep --binary-mode and --local-infile=0. The first disables local client commands such as source and system while reading piped input; the second disables LOAD DATA LOCAL INFILE. These flags do not filter SQL statements. The receiving SQL client is responsible for these protections.

Use a database account limited to the target database rather than a server administrator account. Both the CLI pipe and --sql-output=mysql execute SQL from the source with the permissions of the supplied account. stdout writes the dump unchanged, so the program reading it must choose how to process it.

The three modes:

Mode What happens Output file
file (default) Writes SQL to $STATE_DIR/db.sql db.sql
stdout Streams SQL to stdout, progress/status goes to stderr none
mysql Executes SQL directly against the target database none

Continue an interrupted database import

Run the same command with the same state directory, source address, and target database to continue. Keep the downloaded SQL file unchanged while an apply is unfinished. Do not delete migration state to restart the web server.

Transactional tables can resume from completed work. Keyed MyISAM tables can also resume, but a nontransactional table without a non-null unique key or an interrupted oversized-value update may require aborting and starting the import again. Follow the command’s error instructions for those cases.

See Progress, results, and exit codes when a product runs these commands on a user’s behalf.

Target schema compatibility

The SQL dump keeps the source table definitions, including storage-engine clauses such as ENGINE=MyISAM. Copying a database is not an instruction to upgrade its schema. Storage engines can affect locking, transactions, full-text search, and how IDs are generated.

The destination database has the final say. A MySQL-compatible target may substitute an engine it enforces; Reprint permits that substitution rather than rewriting the engine clause itself. SQLite translates the definitions because it has no MyISAM or InnoDB backend.

For a MyISAM table with IDs numbered separately within each group, a target that enforces InnoDB may need an additional index. Reprint adds that index during import. Existing rows, IDs, and keys are kept, but future IDs use one table-wide sequence. Plugins that rely on per-group numbering may need changes. Reprint reports this change in terminal output, JSONL warning records, and audit.log. It does not change the source database or the downloaded SQL file, and leaves compatible schemas unchanged.

The mysql mode requires --mysql-database and accepts --mysql-host, --mysql-port, --mysql-user, and --mysql-password (or the MYSQL_PASSWORD environment variable). The host string also supports host:port and host:/path/to/socket formats (same as WordPress DB_HOST), but --mysql-port takes precedence when both are specified.

Apply the downloaded database

If the site’s domain is changing (e.g. migrating from https://old-site.com to https://new-site.com), use db-apply with --rewrite-url to import the SQL dump into a target database while rewriting all URLs in one pass.

MySQL target:

php reprint.phar db-apply "$URL" --state-dir="$STATE_DIR" --fs-root="$FS_ROOT" \
    --target-user=site_import --target-db=wp_new \
    --rewrite-url https://old-site.com https://new-site.com

SQLite target:

php reprint.phar db-apply "$URL" --state-dir="$STATE_DIR" --fs-root="$FS_ROOT" \
    --target-engine=sqlite --target-sqlite-path="$STATE_DIR/wordpress.sqlite" \
    --target-db=wp_new \
    --rewrite-url https://old-site.com https://new-site.com

This imports db.sql from the state directory and rewrites URLs in serialized PHP values, JSON, block markup, and plain text while keeping those formats valid. URLs inside deeply nested encoded strings may remain unchanged.

WordPress cleanup after apply

After apply, Reprint clears database-backed WordPress caches for theme paths, permalink rules, update checks, and directory sizes so WordPress can rebuild them for the destination. Later update checks may make network requests.

The selected theme, theme settings, permalink structure, upload paths, scheduled jobs, and locks stay unchanged. This does not load WordPress or flush an external object cache.

Unavailable full-text parsers

For MySQL and MariaDB targets, db-apply and db-pull --sql-output=mysql check named full-text parsers in exported CREATE TABLE statements against the target’s active full-text parser plugins. If a parser is unavailable, Reprint omits that index’s WITH PARSER clause and keeps the index and its columns. The target’s default parser is used instead, so search matches and ranking may change. Supported parsers remain unchanged. The saved db.sql is never edited by this fallback.

Each omitted parser produces a JSONL warning, an audit-log entry, and a message in terminal mode. Callers can detect the warning by its type and reason fields:

{
  "type": "warning",
  "phase": "sql",
  "reason": "unsupported_fulltext_parser",
  "table": "wp_articles",
  "index": "content_search",
  "parser": "ngram",
  "message": "The target has no active full-text parser 'ngram'. Omitting it from index 'content_search' on table 'wp_articles' and using the default parser; search matches and ranking may change."
}

Multiple URL replacements

You can map multiple domains by repeating the flag:

php reprint.phar db-apply "$URL" --state-dir="$STATE_DIR" --fs-root="$FS_ROOT" \
    --target-user=site_import --target-db=wp_new \
    --rewrite-url https://old-site.com https://new-site.com \
    --rewrite-url https://cdn.old-site.com https://cdn.new-site.com

A direct SQL import does not run Reprint’s WordPress cleanup or runtime setup. Use db-apply when you need those steps and their resumable import behavior.

For a database that is already running, see Rewrite URLs in a live database.