sqlconv 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +21 -0
  5. data/README.md +14 -0
  6. data/bin/sqlconv +192 -0
  7. data/sqlconv.gemspec +14 -0
  8. metadata +50 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ad9e122f339ca74831a6c206d8b148fc89e182ca51d33efbd2732d5a14ecd8c5
4
+ data.tar.gz: 54473e10cdd3db7d10db62bdc36d851f76636cf09a6e0ecc9928dca1cf607f97
5
+ SHA512:
6
+ metadata.gz: 77c740a056cba408b3b85d56abfd80444b2c61acc109fc1a982fae6a4cbd5de55ff99043eaff3e747aca5b2d03a791f095120cd49d854b50be9dd04fd31c2423
7
+ data.tar.gz: 2abfb13941ae049ae887509be5c2ddf194d90f085249d01a54830611659decd3a281092d27635eaa849dcccd5b0fbd31adf045848b0f238017a89a17d58e1fda
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Steve Shreeve
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,14 @@
1
+ # sqlconv
2
+
3
+ `sqlconv` is a handy utility, written in Ruby, to massage MySQL dump files.
4
+ It allows columns from a source table to be mapped to a destination table.
5
+
6
+ ## Examples
7
+
8
+ ```shell
9
+ sqlconv.rb 'corporate_users:1,,3-4,8-6,9-,2*now()' users < original-export.sql
10
+ ```
11
+
12
+ ## License
13
+
14
+ This software is licensed under terms of the MIT License.
data/bin/sqlconv ADDED
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ STDOUT.sync = true
4
+
5
+ require 'strscan'
6
+
7
+ # parsing helpers
8
+ class StringScanner
9
+ def scan_for(regx)
10
+ data = scan_until(Regexp === regx ? regx : /#{regx}/)
11
+ size = matched_size or return
12
+ data[-size..-1]
13
+ end
14
+
15
+ def scan_str(str)
16
+ if string[pos, str.size] == str
17
+ self.pos += str.size
18
+ str
19
+ else
20
+ nil
21
+ end
22
+ end
23
+
24
+ def scan_while(regx, skip=nil)
25
+ seen = -1
26
+ list = []
27
+ while item = scan(regx)
28
+ if skip
29
+ list << item if (seen += 1) % skip == 0
30
+ else
31
+ list << item
32
+ end
33
+ end
34
+ list
35
+ end
36
+ end
37
+
38
+ # struct for dealing with selectors
39
+ Selector = Struct.new(:want, :func, :text, :zero, :thru, :reps, :from, :till)
40
+
41
+ # convert user request into selectors
42
+ def grok(want)
43
+ (want || "1-").strip.split(/\s*,\s*/).map do |item|
44
+ item =~ %r!^
45
+ (?:(\d+)\*)?(?: # $1: repeat
46
+ (?:([a-zA-Z]\w*)(\()?)?(?: # $2: function, $3: optional paren
47
+ (?:(['"])(.*?)\4)? | # $4: quote, $5: literal
48
+ (0) | # $6: zero
49
+ ((?>[1-9]\d*))? # $7: from
50
+ ((?<=\d)-|-(?=\d))? # $8: thru
51
+ ((?>[1-9]\d*))? # $9: till
52
+ )\)?
53
+ )$
54
+ !iox or raise "invalid selector item '#{item}'"
55
+ Selector.new(*$~.values_at(0, 2, 5, 6, 8), *$~.values_at(1, 7, 9).map {|e| e&.to_i })
56
+ end or raise "invalid selector '#{want}'"
57
+ end
58
+
59
+ # convert the insert statements
60
+ def conv(tab1, map1, tab2, map2, dump)
61
+ data = StringScanner.new("")
62
+ need = grok(map1)
63
+ cols = nil
64
+ len1 = nil
65
+ len2 = nil
66
+ ours = []
67
+ posn = 0
68
+
69
+ # statement prefix
70
+ pref = [
71
+ "insert into #{tab2 || tab1}",
72
+ (" (#{map2})" if map2),
73
+ " values (",
74
+ ].compact.join
75
+
76
+ # find source table
77
+ data.string = dump.read # dump.read(5000) # TODO: Add streaming support
78
+ into = data.scan_for(/insert into (['"`]?)#{tab1}\1 values /io)
79
+
80
+ # process each line
81
+ loop do
82
+
83
+ # parse insert statements
84
+ if data.scan_str("(") or data.scan_str(into + "(")
85
+ /(?:insert into (['"`]?)#{tab1}\1 values )?\(/io
86
+ cols = data.scan_while(/('.*?(?<!\\)'|(?>[^',()]+)|,)/, 2)
87
+ cols.empty? and (warn "bad sql parse: '#{line}'" or next)
88
+ data.scan(/\)[;,]\s*/)
89
+ else
90
+ break
91
+ end
92
+
93
+ # perform one-time check on source column bounds
94
+ unless len1
95
+ len1 = cols.size
96
+ need.each do |item|
97
+ item.text &&= ["'", item.text.gsub("'", "\\\\'"), "'"].join
98
+ if (len2 = [item.from, item.till, 0].compact.max) > len1
99
+ warn "selector '#{item.want}' referenced source column #{len2}, but only #{len1} are defined"
100
+ cols &&= nil
101
+ end
102
+ end
103
+ cols or exit
104
+ len1 = cols.size
105
+ len2 = nil # we hijacked len2, so clear it
106
+ end
107
+
108
+ # pluck desired columns
109
+ ours.clear
110
+ need.each do |item|
111
+ (item.reps || 1).times do # repeats
112
+ case
113
+ when item.func # function (TODO: honor text/zero/from/till)
114
+ case item.func
115
+ when "rand" then ours.push("'random number here!'")
116
+ when "n","null" then ours.push("null")
117
+ else abort "undefined function '#{item.func}'"
118
+ end
119
+ when item.text# literal
120
+ ours.push(item.text)
121
+ when item.zero # zero
122
+ ours.push(0)
123
+ when item.thru # range
124
+ from = item.from || 1
125
+ till = item.till || len1
126
+ ours.concat case till <=> from
127
+ when 0,1 then cols[(from-1)..(till-1)]
128
+ when -1 then cols[(till-1)..(from-1)].reverse
129
+ end
130
+ when item.from || item.till # one column
131
+ ours.push(cols[(item.from || item.till) - 1])
132
+ else # null
133
+ ours.push("null")
134
+ end
135
+ end
136
+ end
137
+
138
+ # perform one-time check on destination column counts
139
+ unless len2
140
+ if map2 and (len2 = map2.split(",").size) != ours.size
141
+ warn "destination column mismatch (#{len2} defined but #{ours.size} generated)"
142
+ cols &&= nil
143
+ else
144
+ len2 = ours.size
145
+ end
146
+ cols or exit
147
+ end
148
+
149
+ # output insert statement
150
+ puts [pref, ours * ",", ");"].join
151
+ end
152
+ end
153
+
154
+ # ==[ invoke the cli ]==
155
+
156
+ if ARGV.shift =~ /^([a-z][-\w]*):?(.+)?$/
157
+ tab1 = $1
158
+ map1 = $2
159
+ end
160
+
161
+ if ARGV.size > 0 and !File.exists?(ARGV.first)
162
+ if ARGV.shift =~ /^((?>[a-z]?[-\w]*)(?::|$))?(.+)?$/
163
+ $1.to_s.size > 0 and tab2 = $1.chomp(":")
164
+ $2.to_s.size > 0 and map2 = $2.squeeze(',').sub(/^,+/,'').sub(/,+$/,'')
165
+ end
166
+ tab1 = nil if $0.size == 0 # no match, show usage
167
+ end
168
+
169
+ tab1 or (warn [
170
+ "Usage: #{File.basename $0}",
171
+ "<src_table>(:[sel1,sel2,...])",
172
+ "[dst_table][:][col1,col2,...] file"
173
+ ] * ' ' or exit)
174
+
175
+ conv tab1, map1, tab2 || tab1, map2, ARGF
176
+
177
+ __END__
178
+
179
+ # show opt parsing
180
+ p [:tab1, tab1]
181
+ p [:map1, map1]
182
+ p [:tab2, tab2]
183
+ p [:map2, map2]
184
+ p [:argv, ARGV]
185
+ exit
186
+
187
+ # values for testing
188
+ tab1 = 'adonis_schema'
189
+ map1 = nil
190
+ tab2 = nil
191
+ map2 = nil
192
+ ARGV.push 'db1.sql'
data/sqlconv.gemspec ADDED
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "sqlconv"
5
+ s.version = "0.5.0"
6
+ s.author = "Steve Shreeve"
7
+ s.email = "steve.shreeve@gmail.com"
8
+ s.summary = "Handy utility to massage MySQL dump files"
9
+ s.description = "Allows mapping columns from a source to a destination table"
10
+ s.homepage = "https://github.com/shreeve/sqlconv"
11
+ s.license = "MIT"
12
+ s.files = `git ls-files`.split("\n") - %w[.gitignore]
13
+ s.executables = `cd bin && git ls-files .`.split("\n")
14
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sqlconv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Steve Shreeve
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-06-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Allows mapping columns from a source to a destination table
14
+ email: steve.shreeve@gmail.com
15
+ executables:
16
+ - sqlconv
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".ruby-version"
21
+ - Gemfile
22
+ - LICENSE
23
+ - README.md
24
+ - bin/sqlconv
25
+ - sqlconv.gemspec
26
+ homepage: https://github.com/shreeve/sqlconv
27
+ licenses:
28
+ - MIT
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.7.6
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: Handy utility to massage MySQL dump files
50
+ test_files: []