sqlite/test/atrc.c
drh 067b92ba00 Extend the refactoring into extensions. Clean up stray newlines.
FossilOrigin-Name: 7a876209a678a34c198b54ceef9e3c041f128a14dc73357f6a57cadadaa6cf7b
2020-06-19 15:24:12 +00:00

151 lines
4.5 KiB
C

/*
** This program generates a script that stresses the ALTER TABLE statement.
** Compile like this:
**
** gcc -g -c sqlite3.c
** gcc -g -o atrc atrc.c sqlite3.o -ldl -lpthread
**
** Run the program this way:
**
** ./atrc DATABASE | ./sqlite3 DATABASE
**
** This program "atrc" generates a script that can be fed into an ordinary
** command-line shell. The script performs many ALTER TABLE statements,
** runs ".schema --indent" and "PRAGMA integrity_check;", does more
** ALTER TABLE statements to restore the original schema, and then
** runs "PRAGMA integrity_check" again. Every table and column has its
** name changed. The entire script is contained within BEGIN...ROLLBACK
** so that no changes are ever actually made to the database.
*/
#include "sqlite3.h"
#include <stdio.h>
/*
** Generate the text of ALTER TABLE statements that will rename
** every column in table zTable to a generic name composed from
** zColPrefix and a sequential number. The generated text is
** appended pConvert. If pUndo is not NULL, then SQL text that
** will undo the change is appended to pUndo.
**
** The table to be converted must be in the "main" schema.
*/
int rename_all_columns_of_table(
sqlite3 *db, /* Database connection */
const char *zTab, /* Table whose columns should all be renamed */
const char *zColPrefix, /* Prefix for new column names */
sqlite3_str *pConvert, /* Append ALTER TABLE statements here */
sqlite3_str *pUndo /* SQL to undo the change, if not NULL */
){
sqlite3_stmt *pStmt;
int rc;
int cnt = 0;
rc = sqlite3_prepare_v2(db,
"SELECT name FROM pragma_table_info(?1);",
-1, &pStmt, 0);
if( rc ) return rc;
sqlite3_bind_text(pStmt, 1, zTab, -1, SQLITE_STATIC);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zCol = (const char*)sqlite3_column_text(pStmt, 0);
cnt++;
sqlite3_str_appendf(pConvert,
"ALTER TABLE \"%w\" RENAME COLUMN \"%w\" TO \"%w%d\";\n",
zTab, zCol, zColPrefix, cnt
);
if( pUndo ){
sqlite3_str_appendf(pUndo,
"ALTER TABLE \"%w\" RENAME COLUMN \"%w%d\" TO \"%w\";\n",
zTab, zColPrefix, cnt, zCol
);
}
}
sqlite3_finalize(pStmt);
return SQLITE_OK;
}
/* Rename all tables and their columns in the main database
*/
int rename_all_tables(
sqlite3 *db, /* Database connection */
sqlite3_str *pConvert, /* Append SQL to do the rename here */
sqlite3_str *pUndo /* Append SQL to undo the rename here */
){
sqlite3_stmt *pStmt;
int rc;
int cnt = 0;
rc = sqlite3_prepare_v2(db,
"SELECT name FROM sqlite_schema WHERE type='table'"
" AND name NOT LIKE 'sqlite_%';",
-1, &pStmt, 0);
if( rc ) return rc;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zTab = (const char*)sqlite3_column_text(pStmt, 0);
char *zNewTab;
char zPrefix[2];
zPrefix[0] = (cnt%26) + 'a';
zPrefix[1] = 0;
zNewTab = sqlite3_mprintf("tx%d", ++cnt);
if( pUndo ){
sqlite3_str_appendf(pUndo,
"ALTER TABLE \"%s\" RENAME TO \"%w\";\n",
zNewTab, zTab
);
}
rename_all_columns_of_table(db, zTab, zPrefix, pConvert, pUndo);
sqlite3_str_appendf(pConvert,
"ALTER TABLE \"%w\" RENAME TO \"%s\";\n",
zTab, zNewTab
);
sqlite3_free(zNewTab);
}
sqlite3_finalize(pStmt);
return SQLITE_OK;
}
/*
** Generate a script that does this:
**
** (1) Start a transaction
** (2) Rename all tables and columns to use generic names.
** (3) Print the schema after this rename
** (4) Run pragma integrity_check
** (5) Do more ALTER TABLE statements to change the names back
** (6) Run pragma integrity_check again
** (7) Rollback the transaction
*/
int main(int argc, char **argv){
sqlite3 *db;
int rc;
sqlite3_str *pConvert;
sqlite3_str *pUndo;
char *zDbName;
char *zSql1, *zSql2;
if( argc!=2 ){
fprintf(stderr, "Usage: %s DATABASE\n", argv[0]);
}
zDbName = argv[1];
rc = sqlite3_open(zDbName, &db);
if( rc ){
fprintf(stderr, "sqlite3_open() returns %d\n", rc);
return 1;
}
pConvert = sqlite3_str_new(db);
pUndo = sqlite3_str_new(db);
rename_all_tables(db, pConvert, pUndo);
zSql1 = sqlite3_str_finish(pConvert);
zSql2 = sqlite3_str_finish(pUndo);
sqlite3_close(db);
printf("BEGIN;\n");
printf("%s", zSql1);
sqlite3_free(zSql1);
printf(".schema --indent\n");
printf("PRAGMA integrity_check;\n");
printf("%s", zSql2);
sqlite3_free(zSql2);
printf("PRAGMA integrity_check;\n");
printf("ROLLBACK;\n");
return 0;
}