-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconvert2sqlite
executable file
·185 lines (167 loc) · 5.11 KB
/
convert2sqlite
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/awk -f
# Authors: @esperlu, @artemyk, @gkuenning, @dumblob
BEGIN {
if (ARGC != 2) {
printf "%s\n%s\n",
"USAGE: mysql2sqlite.sh dump_mysql.sql > dump_sqlite3.sql",
" file name - (dash) is not supported, because - means stdin" > "/dev/stderr"
err=1 # do not execute the END rule
exit 1
}
FS=",$"
print "PRAGMA synchronous = OFF;"
print "PRAGMA journal_mode = MEMORY;"
print "BEGIN TRANSACTION;"
}
# CREATE TRIGGER statements have funny commenting. Remember we are in trigger.
/^\/\*.*(CREATE.*TRIGGER|create.*trigger)/ {
gsub( /^.*(TRIGGER|trigger)/, "CREATE TRIGGER" )
print
inTrigger = 1
next
}
# The end of CREATE TRIGGER has a stray comment terminator
/(END|end) \*\/;;/ { gsub( /\*\//, "" ); print; inTrigger = 0; next }
# The rest of triggers just get passed through
inTrigger != 0 { print; next }
# CREATE VIEW looks like a TABLE in comments
/^\/\*.*(CREATE.*TABLE|create.*table)/ {
inView = 1
next
}
# The end of CREATE VIEW
/^(\).*(ENGINE|engine).*\*\/;)/ {
inView = 0;
next
}
# The rest of view just get passed through
inView != 0 { next }
# Skip other comments
/^\/\*/ { next }
# Print all `INSERT` lines. The single quotes are protected by another single quote.
( /^ *\(/ && /\) *[,;] *$/ ) || /^(INSERT|insert)/ {
prev = "";
gsub( /\\\047/, "\047\047" ) # single quote
gsub( /\\\047\047,/, "\\\047," )
gsub( /\\n/, "\n" )
gsub( /\\r/, "\r" )
gsub( /\\"/, "\"" )
gsub( /\\\\/, "\\" )
gsub( /\\\032/, "\032" ) # substitute
# sqlite3 is limited to 16 significant digits of precision
while ( match( $0, /0x[0-9a-fA-F]{17}/ ) ) {
hexIssue = 1
sub( /0x[0-9a-fA-F]+/, substr( $0, RSTART, RLENGTH-1 ), $0 )
}
print
next
}
# CREATE DATABASE is not supported
/^(CREATE.*DATABASE|create.*database)/ { next }
# Print the `CREATE` line as is and capture the table name.
/^(CREATE|create)/ {
if ( $0 ~ /IF NOT EXISTS|if not exists/ || $0 ~ /TEMPORARY|temporary/ ){
caseIssue = 1
}
if ( match( $0, /`[^`]+/ ) ) {
tableName = substr( $0, RSTART+1, RLENGTH-1 )
}
aInc = 0
prev = ""
firstInTable = 1
print
next
}
# Replace `FULLTEXT KEY` (probably other `XXXXX KEY`)
/^ (FULLTEXT KEY|fulltext key)/ { gsub( /.+(KEY|key)/, " KEY" ) }
# Get rid of field lengths in KEY lines
/ (PRIMARY |primary )?(KEY|key)/ { gsub( /\([0-9]+\)/, "" ) }
aInc == 1 && /PRIMARY KEY|primary key/ { next }
# Replace COLLATE xxx_xxxx_xx statements with COLLATE BINARY
/ (COLLATE|collate) [a-z0-9_]*/ { gsub( /(COLLATE|collate) [a-z0-9_]*/, "COLLATE BINARY" ) }
# Print all fields definition lines except the `KEY` lines.
/^ / && !/^( (KEY|key)|\);)/ {
if ( match( $0, /[^"`]AUTO_INCREMENT|auto_increment[^"`]/)) {
aInc = 1;
gsub( /AUTO_INCREMENT|auto_increment/, "PRIMARY KEY AUTOINCREMENT" )
}
gsub( /(UNIQUE KEY|unique key) `.*` /, "UNIQUE " )
gsub( /(CHARACTER SET|character set) [^ ]+[ ,]/, "" )
gsub( /DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP|default current_timestamp on update current_timestamp/, "" )
gsub( /(COLLATE|collate) [^ ]+ /, "" )
gsub( /(ENUM|enum)[^)]+\)/, "text " )
gsub( /(SET|set)\([^)]+\)/, "text " )
gsub( /UNSIGNED|unsigned/, "" )
gsub( /` [^ ]*(INT|int)[^ ]*/, "` integer" )
# field comments are not supported
gsub( / (COMMENT|comment).+$/, "" )
# Get commas off end of line
gsub( /,.?$/, "")
if ( prev ){
if ( firstInTable ){
print prev
firstInTable = 0
}
else print "," prev
}
else {
# FIXME check if this is correct in all cases
if ( match( $1,
/(CONSTRAINT|constraint) \".*\" (FOREIGN KEY|foreign key)/ ) )
print ","
}
prev = $1
}
/ ENGINE| engine/ {
if (prev) {
if (firstInTable) {
print prev
firstInTable = 0
}
else print "," prev
# else print prev
}
prev=""
print ");"
next
}
# `KEY` lines are extracted from the `CREATE` block and stored in array for later print
# in a separate `CREATE KEY` command. The index name is prefixed by the table name to
# avoid a sqlite error for duplicate index name.
/^( (KEY|key)|\);)/ {
if (prev) {
if (firstInTable) {
print prev
firstInTable = 0
}
else print "," prev
# else print prev
}
prev = ""
if ($0 == ");"){
print
} else {
if ( match( $0, /`[^`]+/ ) ) {
indexName = substr( $0, RSTART+1, RLENGTH-1 )
}
if ( match( $0, /\([^()]+/ ) ) {
indexKey = substr( $0, RSTART+1, RLENGTH-1 )
}
# idx_ prefix to avoid name clashes (they really happen!)
key[tableName]=key[tableName] "CREATE INDEX \"idx_" tableName "_" indexName "\" ON \"" tableName "\" (" indexKey ");\n"
}
}
END {
if (err) { exit 1};
# print all `KEY` creation lines.
for (table in key) printf key[table]
print "END TRANSACTION;"
if ( hexIssue ){
print "WARN Hexadecimal numbers longer than 16 characters has been trimmed." | "cat >&2"
}
if ( caseIssue ){
print "WARN Pure sqlite identifiers are case insensitive (even if quoted\n" \
" or if ASCII) and doesnt cross-check TABLE and TEMPORARY TABLE\n" \
" identifiers. Thus expect errors like \"table T has no column named F\"." | "cat >&2"
}
}