Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add further tests for RENAME COLUMN. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | alter-table-rename-column |
Files: | files | file ages | folders |
SHA3-256: |
82c4c10a96db76f39edf5f6d027294d4 |
User & Date: | dan 2018-08-20 20:01:01.997 |
Context
2018-08-21
| ||
08:29 | Minor changes to function tokenExpr() in order to claw back cycles lost to the rename-column change. (check-in: 479976955e user: dan tags: alter-table-rename-column) | |
2018-08-20
| ||
20:01 | Add further tests for RENAME COLUMN. (check-in: 82c4c10a96 user: dan tags: alter-table-rename-column) | |
16:16 | Ensure that it is not possible to rename columns of system tables, views or virtual tables. (check-in: 786b5991dc user: dan tags: alter-table-rename-column) | |
Changes
Changes to src/alter.c.
︙ | ︙ | |||
874 875 876 877 878 879 880 | */ zNew = sqlite3NameFromToken(db, pNew); if( !zNew ) goto exit_rename_column; assert( pNew->n>0 ); bQuote = sqlite3Isquote(pNew->z[0]); sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " | | | 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 | */ zNew = sqlite3NameFromToken(db, pNew); if( !zNew ) goto exit_rename_column; assert( pNew->n>0 ); bQuote = sqlite3Isquote(pNew->z[0]); sqlite3NestedParse(pParse, "UPDATE \"%w\".%s SET " "sql = sqlite_rename_column(sql, type, name, %Q, %Q, %d, %Q, %d) " "WHERE name NOT LIKE 'sqlite_%%' AND (type != 'index' OR tbl_name = %Q)" " AND sql NOT LIKE 'create virtual%%'", zDb, MASTER_NAME, zDb, pTab->zName, iCol, zNew, bQuote, pTab->zName ); |
︙ | ︙ | |||
953 954 955 956 957 958 959 | /* ** If there is a RenameToken object associated with parse tree element ** pFrom, then remap that object over to pTo due to a transformation ** in the parse tree. */ void sqlite3MoveRenameToken(Parse *pParse, void *pTo, void *pFrom){ RenameToken *p; | | | 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 | /* ** If there is a RenameToken object associated with parse tree element ** pFrom, then remap that object over to pTo due to a transformation ** in the parse tree. */ void sqlite3MoveRenameToken(Parse *pParse, void *pTo, void *pFrom){ RenameToken *p; for(p=pParse->pRename; ALWAYS(p); p=p->pNext){ if( p->p==pFrom ){ p->p = pTo; break; } } assert( p ); } |
︙ | ︙ | |||
1060 1061 1062 1063 1064 1065 1066 | /* ** An error occured while parsing or otherwise processing a database ** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an ** ALTER TABLE RENAME COLUMN program. The error message emitted by the ** sub-routine is currently stored in pParse->zErrMsg. This function ** adds context to the error message and then stores it in pCtx. */ | | > > > > > | | < < < < < < < < < < | > > | | | | | | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 | /* ** An error occured while parsing or otherwise processing a database ** object (either pParse->pNewTable, pNewIndex or pNewTrigger) as part of an ** ALTER TABLE RENAME COLUMN program. The error message emitted by the ** sub-routine is currently stored in pParse->zErrMsg. This function ** adds context to the error message and then stores it in pCtx. */ static void renameColumnParseError( sqlite3_context *pCtx, sqlite3_value *pType, sqlite3_value *pObject, Parse *pParse ){ const char *zT = sqlite3_value_text(pType); const char *zN = sqlite3_value_text(pObject); char *zErr; zErr = sqlite3_mprintf("error processing %s %s: %s", zT, zN, pParse->zErrMsg); sqlite3_result_error(pCtx, zErr, -1); sqlite3_free(zErr); } /* ** SQL function: ** ** sqlite_rename_column(zSql, iCol, bQuote, zNew, zTable, zOld) ** ** 0. zSql: SQL statement to rewrite ** 1. type: Type of object ("table", "view" etc.) ** 2. object: Name of object ** 3. Database: Database name (e.g. "main") ** 4. Table: Table name ** 5. iCol: Index of column to rename ** 6. zNew: New column name ** 7. bQuote: True if the new column name should be quoted ** ** Do a column rename operation on the CREATE statement given in zSql. ** The iCol-th column (left-most is 0) of table zTable is renamed from zCol ** into zNew. The name should be quoted if bQuote is true. ** ** This function is used internally by the ALTER TABLE RENAME COLUMN command. ** Though accessible to application code, it is not intended for use by |
︙ | ︙ | |||
1116 1117 1118 1119 1120 1121 1122 | int NotUsed, sqlite3_value **argv ){ sqlite3 *db = sqlite3_context_db_handle(context); RenameCtx sCtx; const char *zSql = (const char*)sqlite3_value_text(argv[0]); int nSql = sqlite3_value_bytes(argv[0]); | | | | | | | | | | 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 | int NotUsed, sqlite3_value **argv ){ sqlite3 *db = sqlite3_context_db_handle(context); RenameCtx sCtx; const char *zSql = (const char*)sqlite3_value_text(argv[0]); int nSql = sqlite3_value_bytes(argv[0]); const char *zDb = (const char*)sqlite3_value_text(argv[3]); const char *zTable = (const char*)sqlite3_value_text(argv[4]); int iCol = sqlite3_value_int(argv[5]); const char *zNew = (const char*)sqlite3_value_text(argv[6]); int nNew = sqlite3_value_bytes(argv[6]); int bQuote = sqlite3_value_int(argv[7]); const char *zOld; int rc; char *zErr = 0; Parse sParse; Walker sWalker; Index *pIdx; char *zOut = 0; char *zQuot = 0; /* Quoted version of zNew */ int nQuot = 0; /* Length of zQuot in bytes */ int i; Table *pTab; UNUSED_PARAMETER(NotUsed); if( zSql==0 ) return; if( zTable==0 ) return; if( zNew==0 ) return; if( iCol<0 ) return; pTab = sqlite3FindTable(db, zTable, zDb); if( pTab==0 || iCol>=pTab->nCol ) return; zOld = pTab->aCol[iCol].zName; memset(&sCtx, 0, sizeof(sCtx)); sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol); |
︙ | ︙ | |||
1268 1269 1270 1271 1272 1273 1274 | rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen); } for(pStep=sParse.pNewTrigger->step_list; rc==SQLITE_OK && pStep; pStep=pStep->pNext ){ | > | > > | > | | < | 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 | rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen); } for(pStep=sParse.pNewTrigger->step_list; rc==SQLITE_OK && pStep; pStep=pStep->pNext ){ if( pStep->pSelect ){ sqlite3SelectPrep(&sParse, pStep->pSelect, &sNC); if( sParse.nErr ) rc = sParse.rc; } if( rc==SQLITE_OK && pStep->zTarget ){ Table *pTarget = sqlite3FindTable(db, pStep->zTarget, zDb); if( pTarget==0 ){ rc = SQLITE_ERROR; }else{ SrcList sSrc; memset(&sSrc, 0, sizeof(sSrc)); sSrc.nSrc = 1; sSrc.a[0].zName = pStep->zTarget; sSrc.a[0].pTab = pTarget; sNC.pSrcList = &sSrc; if( pStep->pWhere ){ rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere); } if( rc==SQLITE_OK ){ rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList); } assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) ); if( pStep->pUpsert ){ Upsert *pUpsert = pStep->pUpsert; assert( rc==SQLITE_OK ); rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget); if( rc==SQLITE_OK && pUpsert->pUpsertSet){ ExprList *pUpsertSet = pUpsert->pUpsertSet; rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet); if( rc==SQLITE_OK && pTarget==pTab ){ for(i=0; i<pUpsertSet->nExpr; i++){ char *zName = pUpsertSet->a[i].zName; if( 0==sqlite3_stricmp(zName, zOld) ){ |
︙ | ︙ | |||
1409 1410 1411 1412 1413 1414 1415 | }else{ rc = SQLITE_NOMEM; } renameColumnFunc_done: if( rc!=SQLITE_OK ){ if( sParse.zErrMsg ){ | | | 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 | }else{ rc = SQLITE_NOMEM; } renameColumnFunc_done: if( rc!=SQLITE_OK ){ if( sParse.zErrMsg ){ renameColumnParseError(context, argv[1], argv[2], &sParse); }else{ sqlite3_result_error_code(context, rc); } } if( sParse.pVdbe ){ sqlite3VdbeFinalize(sParse.pVdbe); |
︙ | ︙ | |||
1434 1435 1436 1437 1438 1439 1440 | /* ** Register built-in functions used to help implement ALTER TABLE */ void sqlite3AlterFunctions(void){ static FuncDef aAlterTableFuncs[] = { FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), | | | 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 | /* ** Register built-in functions used to help implement ALTER TABLE */ void sqlite3AlterFunctions(void){ static FuncDef aAlterTableFuncs[] = { FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc), FUNCTION(sqlite_rename_column, 8, 0, 0, renameColumnFunc), #ifndef SQLITE_OMIT_TRIGGER FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc), #endif #ifndef SQLITE_OMIT_FOREIGN_KEY FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc), #endif }; sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs)); } #endif /* SQLITE_ALTER_TABLE */ |
Changes to test/altercol.test.
︙ | ︙ | |||
401 402 403 404 405 406 407 408 409 410 411 412 413 414 | 4 _x_ _xxx_ { { CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) } { CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN INSERT INTO t1 VALUES(new.a, new.b, new._x_) ON CONFLICT (_x_) WHERE _x_>10 DO UPDATE SET _x_ = _x_+1; END } } } { do_rename_column_test 9.$tn $old $new $lSchema } #------------------------------------------------------------------------- # Test that views can be edited even if there are missing collation # sequences or user defined functions. | > > > > > > > > | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | 4 _x_ _xxx_ { { CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) } { CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN INSERT INTO t1 VALUES(new.a, new.b, new._x_) ON CONFLICT (_x_) WHERE _x_>10 DO UPDATE SET _x_ = _x_+1; END } } 4 _x_ _xxx_ { { CREATE TABLE t1(a, b, _x_ INTEGER, PRIMARY KEY(_x_), CHECK(_x_>0)) } { CREATE TRIGGER ttt AFTER UPDATE ON t1 BEGIN INSERT INTO t1 VALUES(new.a, new.b, new._x_) ON CONFLICT (_x_) WHERE _x_>10 DO NOTHING; END } } } { do_rename_column_test 9.$tn $old $new $lSchema } #------------------------------------------------------------------------- # Test that views can be edited even if there are missing collation # sequences or user defined functions. |
︙ | ︙ | |||
468 469 470 471 472 473 474 475 476 477 478 479 480 481 | #------------------------------------------------------------------------- # Test some error conditions: # # 1. Renaming a column of a system table, # 2. Renaming a column of a VIEW, # 3. Renaming a column of a virtual table. # reset_db do_execsql_test 12.1.1 { CREATE TABLE t1(a, b); CREATE INDEX t1a ON t1(a); INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 4); ANALYZE; | > > | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 | #------------------------------------------------------------------------- # Test some error conditions: # # 1. Renaming a column of a system table, # 2. Renaming a column of a VIEW, # 3. Renaming a column of a virtual table. # 4. Renaming a column that does not exist. # 5. Renaming a column of a table that does not exist. # reset_db do_execsql_test 12.1.1 { CREATE TABLE t1(a, b); CREATE INDEX t1a ON t1(a); INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 4); ANALYZE; |
︙ | ︙ | |||
498 499 500 501 502 503 504 | ALTER TABLE v2 RENAME c TO y; } {1 {columns of view v2 may not be renamed}} ifcapable fts5 { do_execsql_test 12.3.1 { CREATE VIRTUAL TABLE ft USING fts5(a, b, c); } | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 | ALTER TABLE v2 RENAME c TO y; } {1 {columns of view v2 may not be renamed}} ifcapable fts5 { do_execsql_test 12.3.1 { CREATE VIRTUAL TABLE ft USING fts5(a, b, c); } do_catchsql_test 12.3.2 { ALTER TABLE ft RENAME a TO z; } {1 {columns of virtual table ft may not be renamed}} } do_execsql_test 12.4.1 { CREATE TABLE t2(x, y, z); } do_catchsql_test 12.4.2 { ALTER TABLE t2 RENAME COLUMN a TO b; } {1 {no such column: "a"}} do_catchsql_test 12.5.1 { ALTER TABLE t3 RENAME COLUMN a TO b; } {1 {no such table: t3}} #------------------------------------------------------------------------- # Test the effect of some parse/resolve errors. # reset_db do_execsql_test 13.1.1 { CREATE TABLE x1(i INTEGER, t TEXT UNIQUE); CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN SELECT * FROM nosuchtable; END; } do_catchsql_test 13.1.2 { ALTER TABLE x1 RENAME COLUMN t TO ttt; } {1 {error processing trigger tr1: no such table: main.nosuchtable}} do_execsql_test 13.1.3 { DROP TRIGGER tr1; CREATE INDEX x1i ON x1(i); SELECT sql FROM sqlite_master WHERE name='x1i'; } {{CREATE INDEX x1i ON x1(i)}} do_execsql_test 13.1.4 { PRAGMA writable_schema = 1; UPDATE sqlite_master SET sql = 'CREATE INDEX x1i ON x1(j)' WHERE name='x1i'; } {} do_catchsql_test 13.1.5 { ALTER TABLE x1 RENAME COLUMN t TO ttt; } {1 {error processing index x1i: no such column: j}} do_execsql_test 13.1.6 { UPDATE sqlite_master SET sql = '' WHERE name='x1i'; } {} do_catchsql_test 13.1.7 { ALTER TABLE x1 RENAME COLUMN t TO ttt; } {1 {database disk image is malformed}} do_execsql_test 13.1.8 { DELETE FROM sqlite_master WHERE name = 'x1i'; } do_execsql_test 13.2.0 { CREATE TABLE data(x UNIQUE, y, z); } foreach {tn trigger error} { 1 { CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN UPDATE data SET x=x+1 WHERE zzz=new.i; END; } {no such column: zzz} 2 { CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN INSERT INTO data(x, y) VALUES(new.i, new.t, 1) ON CONFLICT (x) DO UPDATE SET z=zz+1; END; } {no such column: zz} 3 { CREATE TRIGGER tr1 AFTER INSERT ON x1 BEGIN INSERT INTO x1(i, t) VALUES(new.i+1, new.t||'1') ON CONFLICT (tttttt) DO UPDATE SET t=i+1; END; } {no such column: tttttt} } { do_execsql_test 13.2.$tn.1 " DROP TRIGGER IF EXISTS tr1; $trigger " do_catchsql_test 13.2.$tn.2 { ALTER TABLE x1 RENAME COLUMN t TO ttt; } "1 {error processing trigger tr1: $error}" } #------------------------------------------------------------------------- # Passing invalid parameters directly to sqlite_rename_column(). # do_execsql_test 14.1 { CREATE TABLE ddd(sql, type, object, db, tbl, icol, znew, bquote); INSERT INTO ddd VALUES( 'CREATE TABLE x1(i INTEGER, t TEXT)', 'table', 'x1', 'main', 'x1', -1, 'zzz', 0 ), ( 'CREATE TABLE x1(i INTEGER, t TEXT)', 'table', 'x1', 'main', 'x1', 2, 'zzz', 0 ), ( 'CREATE TABLE x1(i INTEGER, t TEXT)', 'table', 'x1', 'main', 'notable', 0, 'zzz', 0 ); } {} do_execsql_test 14.2 { SELECT sqlite_rename_column(sql, type, object, db, tbl, icol, znew, bquote) FROM ddd; } {{} {} {}} finish_test |
Added test/altermalloc2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | # 2018 August 20 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #************************************************************************* # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl set testprefix altermalloc2 # If SQLITE_OMIT_ALTERTABLE is defined, omit this file. ifcapable !altertable { finish_test return } do_execsql_test 1.0 { CREATE TABLE t1(abcd, efgh); } faultsim_save_and_close do_faultsim_test 1 -prep { faultsim_restore_and_reopen } -body { execsql { ALTER TABLE t1 RENAME abcd TO dcba } } -test { faultsim_test_result {0 {}} } catch {db close} forcedelete test.db sqlite3 db test.db do_execsql_test 2.0 { PRAGMA encoding = 'utf-16'; CREATE TABLE t1(abcd, efgh); } faultsim_save_and_close do_faultsim_test 2 -prep { faultsim_restore_and_reopen } -body { execsql { ALTER TABLE t1 RENAME abcd TO dcba } } -test { faultsim_test_result {0 {}} } reset_db do_execsql_test 3.0 { CREATE TABLE t1(abcd, efgh); CREATE VIEW v1 AS SELECT * FROM t1 WHERE abcd>efgh; } faultsim_save_and_close do_faultsim_test 3 -prep { faultsim_restore_and_reopen } -body { execsql { ALTER TABLE t1 RENAME abcd TO dcba } } -test { faultsim_test_result {0 {}} } finish_test |