คำตอบทั้งหมดนี้ใช้ได้ถ้าคุณดำเนินการทีละบรรทัด อย่างไรก็ตามคำถามเดิมคือการป้อนสคริปต์ sql ที่จะดำเนินการโดยการดำเนินการ db เดียวและวิธีแก้ปัญหาทั้งหมด (เช่นการตรวจสอบเพื่อดูว่าคอลัมน์อยู่ที่นั่นก่อนเวลาหรือไม่) จะต้องใช้โปรแกรมดำเนินการทั้งที่มีความรู้เกี่ยวกับตารางและ คอลัมน์กำลังถูกเปลี่ยนแปลง / เพิ่มหรือทำการประมวลผลล่วงหน้าและแยกวิเคราะห์สคริปต์อินพุตเพื่อกำหนดข้อมูลนี้ โดยปกติคุณจะไม่เรียกใช้สิ่งนี้แบบเรียลไทม์หรือบ่อยครั้ง ดังนั้นแนวคิดในการจับข้อยกเว้นจึงเป็นที่ยอมรับและดำเนินการต่อไป ในนั้นปัญหาคือ ... จะดำเนินต่อไปอย่างไร โชคดีที่ข้อความแสดงข้อผิดพลาดให้ข้อมูลทั้งหมดที่จำเป็นสำหรับเราในการดำเนินการนี้ แนวคิดคือการดำเนินการ sql หากมีข้อยกเว้นในการเรียกตารางการเปลี่ยนแปลงเราสามารถค้นหาบรรทัดเปลี่ยนแปลงตารางใน sql และส่งคืนบรรทัดที่เหลือและดำเนินการจนกว่าจะสำเร็จหรือไม่พบรายการเปลี่ยนแปลงตารางที่ตรงกันอีกต่อไป นี่คือตัวอย่างโค้ดที่เรามีสคริปต์ sql ในอาร์เรย์ เราทำซ้ำอาร์เรย์ที่เรียกใช้แต่ละสคริปต์ เราเรียกมันว่าสองครั้งเพื่อให้คำสั่ง alter table ล้มเหลว แต่โปรแกรมทำได้สำเร็จเพราะเราลบคำสั่ง alter table ออกจาก sql และรันโค้ดที่อัพเดตอีกครั้ง
exec /opt/usr8.6.3/bin/tclsh8.6 "$0" ${1+"$@"}
foreach pkg {sqlite3 } {
if { [ catch {package require {*}$pkg } err ] != 0 } {
puts stderr "Unable to find package $pkg\n$err\n ... adjust your auto_path!";
}
}
array set sqlArray {
1 {
CREATE TABLE IF NOT EXISTS Notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name text,
note text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
);
CREATE TABLE IF NOT EXISTS Version (
id INTEGER PRIMARY KEY AUTOINCREMENT,
version text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
);
INSERT INTO Version(version) values('1.0');
}
2 {
CREATE TABLE IF NOT EXISTS Tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name text,
tag text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
);
ALTER TABLE Notes ADD COLUMN dump text;
INSERT INTO Version(version) values('2.0');
}
3 {
ALTER TABLE Version ADD COLUMN sql text;
INSERT INTO Version(version) values('3.0');
}
}
sqlite3 db :memory:
proc createSchema { sqlArray } {
upvar $sqlArray sql
foreach version [lsort -integer [array names sql ] ] {
set cmd $sql($version)
set ok 0
while { !$ok && [string length $cmd ] } {
try {
db eval $cmd
set ok 1 ;
} on error { err backtrace } {
if { [regexp {duplicate column name: ([a-zA-Z0-9])} [string trim $err ] match columnname ] } {
puts "Error: $err ... trying again"
set cmd [removeAlterTable $cmd $columnname ]
} else {
throw DBERROR "$err\n$backtrace"
}
}
}
}
}
proc removeAlterTable { sqltext columnname } {
set mode skip
set result [list]
foreach line [split $sqltext \n ] {
if { [string first "alter table" [string tolower [string trim $line] ] ] >= 0 } {
if { [string first $columnname $line ] } {
set mode add
continue;
}
}
if { $mode eq "add" } {
lappend result $line
}
}
if { $mode eq "skip" } {
puts stderr "Unable to find matching alter table line"
return ""
} elseif { [llength $result ] } {
return [ join $result \n ]
} else {
return ""
}
}
proc printSchema { } {
db eval { select * from sqlite_master } x {
puts "Table: $x(tbl_name)"
puts "$x(sql)"
puts "
}
}
createSchema sqlArray
printSchema
createSchema sqlArray
printSchema
ผลลัพธ์ที่คาดหวัง
Table: Notes
CREATE TABLE Notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name text,
note text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
, dump text)
Table: sqlite_sequence
CREATE TABLE sqlite_sequence(name,seq)
Table: Version
CREATE TABLE Version (
id INTEGER PRIMARY KEY AUTOINCREMENT,
version text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
, sql text)
Table: Tags
CREATE TABLE Tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name text,
tag text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
)
Error: duplicate column name: dump ... trying again
Error: duplicate column name: sql ... trying again
Table: Notes
CREATE TABLE Notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name text,
note text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
, dump text)
Table: sqlite_sequence
CREATE TABLE sqlite_sequence(name,seq)
Table: Version
CREATE TABLE Version (
id INTEGER PRIMARY KEY AUTOINCREMENT,
version text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
, sql text)
Table: Tags
CREATE TABLE Tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name text,
tag text,
createdDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) ) ,
updatedDate integer(4) DEFAULT ( cast( strftime('%s', 'now') as int ) )
)
user_version
คืออะไร? ฉันถือว่าเป็นศูนย์ แต่คงจะดีหากได้เห็นเอกสารนั้น