sqldump 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ foo.sqlite
2
+ tmp
3
+ .idea
4
+ doc
5
+ *.gem
6
+ .bundle
7
+ Gemfile.lock
8
+ pkg/*
9
+ *~
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,8 @@
1
+ Version 0.0.1
2
+ -------------
3
+
4
+ Initial version.
5
+ - Very simple INSERT support.
6
+ - CSV support.
7
+ - SQLite3
8
+ - PostgreSQL
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in sqldump.gemspec
4
+ gemspec
@@ -0,0 +1,6 @@
1
+ guard 'rspec' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch(%r{^lib/(.+)/(.+)\.rb$}) { |m| "spec/#{m[1]}/#{m[2]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Mats Sigge
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,105 @@
1
+ # SQLDump
2
+
3
+ A command line tool to dump the data in a database table as INSERT or UPDATE statements, or in CSV format.
4
+
5
+ ## Installation
6
+
7
+ Just install the gem with
8
+
9
+ ```bash
10
+ $ gem install sqldump
11
+ ```
12
+
13
+ This will make the executable `sqldump` available from your command line.
14
+
15
+ **Requires Ruby 1.9.2 or later.**
16
+
17
+ ## Usage
18
+
19
+ ###Simplest case (SQLite3 is default). Dumps in csv format.
20
+
21
+ ```bash
22
+ $ sqldump -d mydatabase.sqlite3 mytable
23
+ ```
24
+
25
+ ###Dump as INSERT statements
26
+
27
+ ```bash
28
+ $ sqldump -d mydatabase.sqlite3 -i mytable
29
+ ```
30
+
31
+ ###Postgres database with username and password
32
+
33
+ ```bash
34
+ $ sqldump -T pg -d mypostgresdb -U username -P password -i mytable
35
+ ```
36
+
37
+ ###Show all options
38
+
39
+ ```bash
40
+ $ sqldump -h
41
+ ```
42
+
43
+ ## Development
44
+
45
+ Questions or problems? Please post them on the [issue tracker](https://github.com/matssigge/sqldump/issues). You can contribute changes by forking the project and submitting a pull request. You can ensure the tests passing by running `bundle` and `rake`.
46
+
47
+ This gem is created by Mats Sigge and is under the MIT License.
48
+
49
+ ## Roadmap
50
+
51
+ ###Immediate goals
52
+ * More databases. At least MySQL and SQL Server.
53
+ * Selecting only some columns
54
+ * Support for UPDATEs
55
+ * Pretty-printing SQL on multiple lines
56
+ * Suppressing NULL columns in output
57
+ * Handling auto-incrementing columns, e.g. nextval('sequence_name') in PostgreSQL
58
+
59
+ ###Longer-term goals / pipe dreams
60
+ * Configuration system, e.g. .sqldump file
61
+ ** Default user, password, host, database
62
+ * "Column handlers", e.g. some way of saying that the column CREATED_AT should get the value now() or getDate() instead of whatever value is actually in the database.
63
+ * Other hooks, e.g. being able to append `SELECT @id = @@IDENTITY` after a select on SQL Server.
64
+
65
+ ###Done
66
+ * Initial support for dumping INSERTs
67
+ * Support for SQLite3
68
+ * Support for PostgreSQL
69
+
70
+ ## History
71
+
72
+ Once, at a company where I worked, there was a whole lot of DB scripting. So much that I got tired of writing all that SQL by hand. At first, there were some SQL scripts that generated SQL as output, i.e. something like
73
+
74
+ ```SQL
75
+ SELECT 'INSERT INTO ' + ...
76
+ ```
77
+
78
+ but that gets tired *really quickly*. So after a while, I started hacking on a perl tool called SQLDump. It would query the database and output the results as INSERT (or UPDATE) statements directly. More and more options were added on, and it got to be quite powerful. It could take a command line like
79
+
80
+ ```bash
81
+ $ sqldump -S testserver -d test_db_14 -irtl -f SPROCKET_ID RATCHET WHERE RATCHET_DATE = '2012-01-02'
82
+ ```
83
+
84
+ and output
85
+
86
+ ```SQL
87
+ INSERT INTO RATCHET (
88
+ SPROCKET_ID,
89
+ RATCHET_NAME,
90
+ RATCHET_DATE
91
+ CREATED_AT
92
+ )
93
+ VALUES (
94
+ @SPROCKET_ID,
95
+ 'My ratchet rocks!',
96
+ '2012-01-02',
97
+ getDate()
98
+ )
99
+ ```
100
+
101
+ which was pretty useful at the time. I could chain together sequences of calls and dump whole hierarchies of data. I used it a lot.
102
+
103
+ It also ended up a big smelly mess. And while it's possible to make a mess in any language, it's one of the things that Perl does best. (Don't get me wrong, I love Perl, and can't imagíne life without it, but I've also written some of my worst code in it.) So I decided that I wanted to rewrite it in Ruby, and at least attempting to keep the code clean. Also, the initial tool was specific to both SQL Server and the idiosynchracies of the system I worked on (i.e. special handling of some column names and other stuff). My vision of this rewrite is to be able to support the same kind of special handling, but through some form of hooks instead of hard coding that logic into the guts of the tool.
104
+
105
+ Mats Sigge
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
3
+ require 'sqldump'
4
+
5
+ options = Sqldump::Options.new(ARGV)
6
+ connector = Sqldump::Connector.new(options)
7
+ connector.connect do |dbh|
8
+ dumper = Sqldump::Dumper.new(dbh, options)
9
+ dumper.dump do |sth|
10
+ formatter = Sqldump::Formatter.formatter(sth, STDOUT, options)
11
+ formatter.output
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ Feature: dump data from different database types (e.g. MySQL, PostgreSQL, SQLite, etc.)
2
+
3
+ sqldump supports dumping data from any database type supported by Ruby DBI/DBD.
4
+ The default database type is (for now) SQLite3 for the simple reason that it's extremely easy to test with.
5
+
6
+ In a distant future, there may be JRuby support for JDBC to (possibly) extend the range of supported databases
7
+ even further.
8
+
9
+ Scenario: SQLite3
10
+
11
+ Scenario: MySQL
12
+
13
+ Scenario: SQL Server
14
+
15
+ Scenario: PostgreSQL
16
+ Given a PostgreSQL database named "numbers" on "localhost"
17
+ And a table "numbers" with the following data
18
+ | number[int] |
19
+ | 17 |
20
+ | 42 |
21
+ | 4711 |
22
+ When I run `sqldump -T postgresql -d numbers -S localhost -U mats numbers`
23
+ Then it should pass with:
24
+ """
25
+ 17
26
+ 42
27
+ 4711
28
+ """
@@ -0,0 +1,57 @@
1
+ Feature: dump as csv
2
+
3
+ The default mode of operation of sqldump is to output data in CSV
4
+
5
+ Scenario: one-column, one-row table
6
+ Given a database "foo.sqlite" with a table "number" with the following data
7
+ | number[int] |
8
+ | 42 |
9
+ When I run `sqldump -d foo.sqlite number`
10
+ Then it should pass with:
11
+ """
12
+ 42
13
+ """
14
+
15
+ Scenario: multi-row table
16
+ Given a database "foo.sqlite" with a table "numbers" with the following data
17
+ | number[int] |
18
+ | 17 |
19
+ | 42 |
20
+ | 4711 |
21
+ When I run `sqldump -d foo.sqlite numbers`
22
+ Then it should pass with:
23
+ """
24
+ 17
25
+ 42
26
+ 4711
27
+ """
28
+
29
+ Scenario: multi-column table
30
+ Given a database "foo.sqlite" with a table "numbers_and_strings" with the following data
31
+ | number[int] | string |
32
+ | 17 | foo |
33
+ | 42 | bar |
34
+ | 4711 | baz |
35
+ When I run `sqldump -d foo.sqlite numbers_and_strings`
36
+ Then it should pass with:
37
+ """
38
+ 17,foo
39
+ 42,bar
40
+ 4711,baz
41
+ """
42
+
43
+ Scenario: with header line
44
+ Given a database "foo.sqlite" with a table "numbers_strings_and_things" with the following data
45
+ | number[int] | string | thing |
46
+ | 17 | foo | rubber duck |
47
+ When I run `sqldump -d foo.sqlite -H numbers_strings_and_things`
48
+ Then it should pass with:
49
+ """
50
+ number,string,thing
51
+ 17,foo,rubber duck
52
+ """
53
+
54
+ Scenario: custom separator
55
+
56
+
57
+ Scenario: quoting of separator
@@ -0,0 +1,23 @@
1
+ Feature: dump data as INSERT statements
2
+
3
+ sqldump can dump data as INSERT statements so that the corresponding data can be inserted in a new database
4
+
5
+ Scenario: simple table
6
+ Given a database "foo.sqlite" with a table "number" with the following data
7
+ | number[int] |
8
+ | 42 |
9
+ When I run `sqldump -d foo.sqlite -i number`
10
+ Then it should pass with:
11
+ """
12
+ INSERT INTO number (number) VALUES (42);
13
+ """
14
+
15
+ Scenario: null value
16
+ Given a database "foo.sqlite" with a table "number" with the following data
17
+ | number[int] |
18
+ | <null> |
19
+ When I run `sqldump -d foo.sqlite -i number`
20
+ Then it should pass with:
21
+ """
22
+ INSERT INTO number (number) VALUES (NULL);
23
+ """
@@ -0,0 +1,15 @@
1
+ require 'dbi'
2
+
3
+ Given /^a database "([^"]*)" with a table "([^"]*)" with the following data$/ do |database, table_name, table|
4
+ create_dummy_database_with_data(:sqlite3, nil, database, table_name, table)
5
+ end
6
+
7
+ Given /^a PostgreSQL database named "([^"]*)" on "([^"]*)"$/ do |database, host|
8
+ @database = database
9
+ @host = host
10
+ @driver = :postgresql
11
+ end
12
+
13
+ When /^a table "([^"]*)" with the following data$/ do |table_name, table|
14
+ create_dummy_database_with_data(@driver, @host, @database, table_name, table)
15
+ end
@@ -0,0 +1,62 @@
1
+ class CukeTableDataConverter
2
+
3
+ def initialize(table_name, table)
4
+ @table_name = table_name
5
+ @table = table
6
+
7
+ @column_names = extract_column_names(table)
8
+ @datatypes = extract_column_datatypes(table)
9
+ end
10
+
11
+ def extract_column_names(table)
12
+ table.column_names.collect { |column| column_name(column) }
13
+ end
14
+
15
+ def extract_column_datatypes(table)
16
+ datatypes = Hash.new
17
+ table.column_names.each do |column|
18
+ datatype = column_datatype(column)
19
+ datatypes[column_name(column)] = datatype
20
+ end
21
+ datatypes
22
+ end
23
+
24
+ def get_create_table_sql
25
+ query = "CREATE TABLE #{@table_name} ("
26
+ query += @column_names.collect { |column| "#{column} #{@datatypes[column]}" }.join ", "
27
+ query += ");"
28
+ query
29
+ end
30
+
31
+ def get_insert_sql
32
+ sql = []
33
+ @table.hashes.each do |hash|
34
+ query = "INSERT INTO #{@table_name} ("
35
+ query += @column_names.join ", "
36
+ query += ") VALUES ("
37
+ query += @table.column_names.collect { |column| as_sql_literal(hash[column]) }.join ", "
38
+ query += ");"
39
+ sql << query
40
+ end
41
+ sql
42
+ end
43
+
44
+ def as_sql_literal(value)
45
+ if value == '<null>'
46
+ 'NULL'
47
+ elsif (value =~ /[^0-9]/ || value =~ /^0./)
48
+ "'#{value}'"
49
+ else
50
+ value
51
+ end
52
+ end
53
+
54
+ def column_name(column_heading)
55
+ /^(\w+)/.match(column_heading)[1]
56
+ end
57
+
58
+ def column_datatype(column_heading)
59
+ column_heading =~ /\[([^\]]+)\]/ ? $1 : 'varchar(100)'
60
+ end
61
+
62
+ end
@@ -0,0 +1,66 @@
1
+ $LOAD_PATH << File.expand_path('../../../lib', __FILE__)
2
+ require 'aruba/cucumber'
3
+ # Ensure aruba can find the bin folder
4
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
5
+
6
+ def drop_database_if_exists(driver, host, database, username, password)
7
+ case driver
8
+ when :sqlite3
9
+ File.delete(database) if File.exists?(database)
10
+ when :postgresql
11
+ DBI.connect("DBI:Pg:postgres:#{host}", username, password) do |dbh|
12
+ dbh.do("DROP DATABASE IF EXISTS #{database}")
13
+ end
14
+ end
15
+ end
16
+
17
+ def get_driver_string(driver)
18
+ driver_string = case driver
19
+ when :sqlite3
20
+ "SQLite3"
21
+ when :postgresql
22
+ "Pg"
23
+ end
24
+ driver_string
25
+ end
26
+
27
+ def create_and_connect_to_database(driver, host, database, username, password, &block)
28
+ create_database(driver, host, database, username, password)
29
+
30
+ url = "DBI:#{get_driver_string(driver)}:#{database}"
31
+ url += ":#{host}" if host
32
+
33
+ if block
34
+ DBI.connect(url, username, password, nil, &block)
35
+ end
36
+ DBI.connect(url, username, password)
37
+ end
38
+
39
+ def create_database(driver, host, database, username, password)
40
+ case driver
41
+ when :sqlite3
42
+ # Do nothing - just connecting to the correct filename creates the database
43
+ when :postgresql
44
+ DBI.connect("DBI:Pg:postgres:#{host}", username, password) do |dbh|
45
+ dbh.do("CREATE DATABASE #{database}")
46
+ end
47
+ end
48
+ end
49
+
50
+ def create_dummy_database_with_data(driver, host, database, table_name, table_data)
51
+ username = 'mats'
52
+ password = nil
53
+
54
+ in_current_dir do
55
+ drop_database_if_exists(driver, host, database, username, password)
56
+
57
+ converter = CukeTableDataConverter.new(table_name, table_data)
58
+
59
+ create_and_connect_to_database(driver, host, database, username, password) do |dbh|
60
+ dbh.do(converter.get_create_table_sql)
61
+ converter.get_insert_sql.each do |sql|
62
+ dbh.do(sql)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,4 @@
1
+ require 'sqldump/options'
2
+ require 'sqldump/connector'
3
+ require 'sqldump/dumper'
4
+ require 'sqldump/formatter'
@@ -0,0 +1,47 @@
1
+ require 'dbi'
2
+
3
+ module Sqldump
4
+
5
+ class Connector
6
+
7
+ def initialize(options)
8
+ @options = options
9
+ end
10
+
11
+ def connect
12
+ begin
13
+ dbh = DBI.connect(get_url, get_user, get_password)
14
+ yield dbh
15
+
16
+ ensure
17
+ dbh.disconnect if dbh
18
+ end
19
+
20
+ end
21
+
22
+ def get_url
23
+ database = @options.database
24
+ driver = get_driver
25
+ "DBI:#{driver}:#{database}"
26
+ end
27
+
28
+ def get_driver
29
+ case @options.database_type
30
+ when :sqlite3
31
+ "SQLite3"
32
+ when :postgresql
33
+ "Pg"
34
+ end
35
+ end
36
+
37
+ def get_user
38
+ @options.username
39
+ end
40
+
41
+ def get_password
42
+ @options.password
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,21 @@
1
+ module Sqldump
2
+
3
+ class CsvFormatter
4
+
5
+ def initialize(sth, io, options)
6
+ @sth = sth
7
+ @io = io
8
+ @options = options
9
+ end
10
+
11
+ def output
12
+ if @options.csv_header
13
+ @io.puts @sth.column_names.join(",")
14
+ end
15
+ @sth.fetch do |row|
16
+ @io.puts row.join(",")
17
+ end
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,23 @@
1
+ module Sqldump
2
+
3
+ class Dumper
4
+
5
+ def initialize(dbh, options)
6
+ @dbh = dbh
7
+ @options = options
8
+ end
9
+
10
+ def dump
11
+ begin
12
+ sth = @dbh.execute(@options.sql)
13
+ yield sth
14
+
15
+ ensure
16
+ sth.finish if sth
17
+
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,21 @@
1
+ require 'sqldump/csv_formatter'
2
+ require 'sqldump/insert_formatter'
3
+
4
+ module Sqldump
5
+
6
+ class Formatter
7
+
8
+ private_class_method :new
9
+
10
+ def self.formatter(sth, io, options)
11
+ case options.dump_mode
12
+ when :csv
13
+ CsvFormatter.new(sth, io, options)
14
+ when :insert
15
+ InsertFormatter.new(sth, io, options)
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,59 @@
1
+ module Sqldump
2
+
3
+ class InsertFormatter
4
+
5
+ def initialize(sth, io, options)
6
+ @sth = sth
7
+ @io = io
8
+ @options = options
9
+
10
+ setup_column_type_mapping()
11
+ end
12
+
13
+ def setup_column_type_mapping
14
+ @column_type_by_name = Hash[@sth.column_names.zip(@sth.column_types)]
15
+ end
16
+
17
+ def column_type(column_name)
18
+ @column_type_by_name[column_name]
19
+ end
20
+
21
+ def output_column_names
22
+ @io.print @sth.column_names.join(", ")
23
+ end
24
+
25
+ def output_values(row)
26
+ quoted_list = []
27
+ row.each_with_name do |value, column_name|
28
+ quoted_list.push quote(value, column_name)
29
+ end
30
+ @io.print quoted_list.join(", ")
31
+ end
32
+
33
+ def output
34
+ @sth.fetch do |row|
35
+ @io.print("INSERT INTO #{@options.table} (")
36
+ output_column_names()
37
+ @io.print ") VALUES ("
38
+ output_values(row)
39
+ @io.print ");\n"
40
+ end
41
+ end
42
+
43
+ def quote(value, column_name)
44
+ if value.nil?
45
+ "NULL"
46
+ else
47
+ type = column_type(column_name)
48
+ if type == DBI::Type::Integer or type == DBI::Type::Float or type == DBI::Type::Decimal
49
+ value
50
+ else
51
+ value = value.to_s.gsub(/'/, "''")
52
+ "'#{value}'"
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,90 @@
1
+ require 'optparse'
2
+
3
+ module Sqldump
4
+
5
+ class Options
6
+
7
+ attr_accessor :database
8
+ attr_accessor :host
9
+ attr_accessor :username
10
+ attr_accessor :password
11
+ attr_accessor :database_type
12
+ attr_accessor :table
13
+ attr_accessor :sql
14
+ attr_accessor :csv_header
15
+ attr_accessor :dump_mode
16
+
17
+ def initialize(argv)
18
+ parse_options(argv)
19
+
20
+ set_derived_options(argv)
21
+ end
22
+
23
+ def set_derived_options(argv)
24
+ self.table = argv[0]
25
+
26
+ self.sql = "select * from " + argv.join(" ")
27
+ end
28
+
29
+ def parse_options(argv)
30
+ optparse = define_options()
31
+ optparse.parse!(argv)
32
+ if argv.size == 0
33
+ print optparse
34
+ exit
35
+ end
36
+ end
37
+
38
+ def setup_defaults
39
+ self.dump_mode = :csv
40
+ self.database_type = :sqlite3
41
+ self.host = 'localhost'
42
+ end
43
+
44
+ def define_options
45
+ optparse = OptionParser.new do |opts|
46
+ setup_defaults()
47
+
48
+ opts.banner = "Usage: sqldump [options] table [extra sql]"
49
+
50
+ opts.on_head('-h', '--help', 'Display this help.') do
51
+ puts opts
52
+ exit
53
+ end
54
+
55
+ # TODO: Handle line wrapping in description
56
+ opts.on('-d', '--database DATABASE', "Specify the database to dump data from. In the case of file-based databases, this should be the full path of the database file.") do |database|
57
+ self.database = database
58
+ end
59
+
60
+ opts.on('-S', '--host USER', 'Specifies the host where the database is located, if applicable. If not specified, the default host is localhost.') do |host|
61
+ self.host = host
62
+ end
63
+
64
+ opts.on('-U', '--username USER', 'Specifies the username to use') do |username|
65
+ self.username = username
66
+ end
67
+
68
+ opts.on('-P', '--password PASSWORD', 'Specifies the password to use') do |password|
69
+ self.password = password
70
+ end
71
+
72
+ opts.on('-T', '--dbtype TYPE', 'Specify the type of database to connect to. Supported types are sqlite3, postgresql/pg.') do |type|
73
+ type = 'postgresql' if type == 'pg'
74
+ self.database_type = type.to_sym
75
+ end
76
+
77
+ opts.on('-i', '--insert', 'Dump data as INSERT statements.') do
78
+ self.dump_mode = :insert
79
+ end
80
+
81
+ opts.on('-H', '--header', 'Include column names in csv mode.') do
82
+ self.csv_header = true
83
+ end
84
+ end
85
+ optparse
86
+ end
87
+
88
+ end
89
+
90
+ end
@@ -0,0 +1,3 @@
1
+ module Sqldump
2
+ VERSION = "0.0.1"
3
+ end
data/numbers ADDED
File without changes
@@ -0,0 +1,15 @@
1
+ require 'sqldump'
2
+ require 'dbi'
3
+
4
+ class Object
5
+
6
+ def create_dummy_database(database = '/tmp/foo.sqlite')
7
+ File.delete(database) if File.exists?(database)
8
+
9
+ dbh = DBI.connect("DBI:SQLite3:#{database}")
10
+ dbh.do("CREATE TABLE numbers (number int);")
11
+ dbh.do("INSERT INTO numbers (number) VALUES (42);")
12
+ dbh
13
+ end
14
+
15
+ end
@@ -0,0 +1,67 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ module Sqldump
5
+
6
+ describe 'Connector' do
7
+
8
+ before(:each) do
9
+ @options = double('options')
10
+ @options.stub(:database).and_return('database')
11
+ @options.stub(:database_type).and_return(:sqlite3)
12
+ @options.stub(:username).and_return(nil)
13
+ @options.stub(:password).and_return(nil)
14
+ end
15
+
16
+ describe '#connect' do
17
+
18
+ it 'connects to the specified database' do
19
+ database = "/tmp/foo.sqlite"
20
+ dbh = create_dummy_database(database)
21
+ dbh.disconnect
22
+
23
+ @options.stub(:database).and_return(database)
24
+
25
+ connector = Connector.new(@options)
26
+ connector.connect do |dbh|
27
+ sth = dbh.execute("SELECT * FROM numbers")
28
+ sth.fetch do |row|
29
+ row[0].should == 42
30
+ end
31
+ sth.finish
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ describe '#get_url' do
38
+ it 'uses the Pg driver when postgresql is specified' do
39
+ @options.stub(:database).and_return('numbers')
40
+ @options.stub(:database_type).and_return(:postgresql)
41
+
42
+ connector = Connector.new(@options)
43
+ connector.get_url.should == 'DBI:Pg:numbers'
44
+ end
45
+ end
46
+
47
+ describe '#get_user' do
48
+ it 'returns the specified password' do
49
+ @options.stub(:username).and_return('the_user')
50
+
51
+ connector = Connector.new(@options)
52
+ connector.get_user.should == 'the_user'
53
+ end
54
+ end
55
+
56
+ describe '#get_user' do
57
+ it 'returns the specified password' do
58
+ @options.stub(:password).and_return('top_secret')
59
+
60
+ connector = Connector.new(@options)
61
+ connector.get_password.should == 'top_secret'
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,43 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+ require 'dbi'
4
+
5
+ module Sqldump
6
+
7
+ describe "CsvFormatter" do
8
+
9
+ def formatter_example(csv_header_option, expected_result)
10
+ strio = StringIO.new
11
+
12
+ options = double("Options")
13
+ options.stub(:csv_header).and_return(csv_header_option)
14
+
15
+ formatter = CsvFormatter.new(@sth, strio, options)
16
+ formatter.output
17
+ strio.close
18
+ strio.string.should == expected_result
19
+ end
20
+
21
+ before(:each) do
22
+ @dbh = create_dummy_database
23
+ @dbh.do("create table numbers_and_strings (number int, string varchar(100));")
24
+ @dbh.do("insert into numbers_and_strings values (42, 'thingy')")
25
+ @sth = @dbh.execute "select * from numbers_and_strings"
26
+ end
27
+
28
+ after(:each) do
29
+ @sth.finish
30
+ @dbh.disconnect
31
+ end
32
+
33
+ it "prints column values verbatim, semicolon-separated, to supplied IO object" do
34
+ formatter_example(false, "42,thingy\n")
35
+ end
36
+
37
+ it "prints column headings when given csv_header flag" do
38
+ formatter_example(true, "number,string\n42,thingy\n")
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,28 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ module Sqldump
5
+
6
+ describe 'Dumper' do
7
+
8
+ describe 'Dump' do
9
+
10
+ it 'Executes the select and passes an open statement handle to the supplied block' do
11
+ dbh = create_dummy_database
12
+
13
+ options = double('options')
14
+ options.stub(:sql).and_return("select * from numbers")
15
+
16
+ dumper = Dumper.new(dbh, options)
17
+ dumper.dump do |sth|
18
+ sth.fetch do |row|
19
+ row[0].should == 42
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,36 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ module Sqldump
5
+
6
+ describe Formatter do
7
+
8
+ context "dump_mode is :csv" do
9
+
10
+ it "creates a CsvFormatter" do
11
+ options = double("Options")
12
+ options.stub(:dump_mode).and_return(:csv)
13
+ formatter = Formatter.formatter(nil, nil, options)
14
+ formatter.class.should == CsvFormatter
15
+ end
16
+
17
+ end
18
+
19
+ context "dump_mode is :insert" do
20
+
21
+ class InsertFormatter
22
+ def setup_column_type_mapping
23
+ @column_type_by_name = {}
24
+ end
25
+ end
26
+
27
+ it "creates an InsertFormatter" do
28
+ options = double("Options")
29
+ options.stub(:dump_mode).and_return(:insert)
30
+ formatter = Formatter.formatter(double("sth").as_null_object, nil, options)
31
+ formatter.class.should == InsertFormatter
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,69 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+ require 'dbi'
4
+
5
+ module Sqldump
6
+
7
+ describe InsertFormatter do
8
+
9
+ describe "#output" do
10
+
11
+ def formatter_example(expected_result)
12
+ strio = StringIO.new
13
+
14
+ options = double("Options")
15
+ options.stub(:table).and_return('numbers_and_strings')
16
+
17
+ formatter = InsertFormatter.new(@sth, strio, options)
18
+ formatter.output
19
+ strio.close
20
+
21
+ strio.string.should == expected_result
22
+ end
23
+
24
+ before(:each) do
25
+ @dbh = create_dummy_database
26
+ @dbh.do("create table numbers_and_strings (number int, string varchar(100));")
27
+ @dbh.do("insert into numbers_and_strings values (42, 'thingy')")
28
+ @sth = @dbh.execute "select * from numbers_and_strings"
29
+ end
30
+
31
+ after(:each) do
32
+ @sth.finish
33
+ @dbh.disconnect
34
+ end
35
+
36
+ it "creates an insert statement for each row of the table" do
37
+ formatter_example("INSERT INTO numbers_and_strings (number, string) VALUES (42, 'thingy');\n")
38
+ end
39
+
40
+ end
41
+
42
+ describe "#quote" do
43
+
44
+ class InsertFormatter
45
+ def setup_column_type_mapping
46
+ @column_type_by_name = {
47
+ "number" => DBI::Type::Integer,
48
+ "string" => DBI::Type::Varchar
49
+ }
50
+ end
51
+ end
52
+
53
+ before(:each) do
54
+ @formatter = InsertFormatter.new(nil, nil, nil)
55
+ end
56
+
57
+ it "quotes a string" do
58
+ @formatter.quote("thing", "string").should == "'thing'"
59
+ end
60
+
61
+ it "doesn't quote an integer" do
62
+ @formatter.quote(17, "number").should == 17
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,111 @@
1
+ require 'rspec'
2
+ require 'spec_helper'
3
+
4
+ def make_options(args)
5
+ args << 'table' unless args[-1] == 'table'
6
+ Sqldump::Options.new(args)
7
+ end
8
+
9
+ module Sqldump
10
+
11
+ describe 'Options handler' do
12
+
13
+
14
+ describe 'database and table' do
15
+ let(:options) { make_options(%w(-d dbname table)) }
16
+
17
+ it "extracts the database name" do
18
+ options.database.should == 'dbname'
19
+ end
20
+
21
+ it "extracts the table name" do
22
+ options.table.should == 'table'
23
+ end
24
+
25
+ it "generates select sql" do
26
+ options.sql.should match /select \* from table/i
27
+ end
28
+ end
29
+
30
+ describe 'host' do
31
+ it 'sets the host to localhost if not specified' do
32
+ options = make_options([])
33
+ options.host.should == 'localhost'
34
+ end
35
+
36
+ it 'sets the host to what is specified' do
37
+ options = make_options(%w(-S server.example.com))
38
+ options.host.should == 'server.example.com'
39
+ end
40
+ end
41
+
42
+ describe 'database type' do
43
+ it 'sets the default to SQLite3' do
44
+ options = make_options([])
45
+ options.database_type.should == :sqlite3
46
+ end
47
+
48
+ it 'sets the type to postgresql if specified' do
49
+ options = make_options(%w(-T postgresql))
50
+ options.database_type.should == :postgresql
51
+ end
52
+
53
+ it 'pg is an alias for postgresql' do
54
+ options = make_options(%w(-T pg))
55
+ options.database_type.should == :postgresql
56
+ end
57
+ end
58
+
59
+ describe 'username' do
60
+ it 'sets the username to nil if not specified' do
61
+ options = make_options([])
62
+ options.username.should be_nil
63
+ end
64
+
65
+ it 'sets the username to what is specified' do
66
+ options = make_options(%w(-U the_user))
67
+ options.username.should == 'the_user'
68
+ end
69
+ end
70
+
71
+ describe 'password' do
72
+ it 'sets the password to nil if not specified' do
73
+ options = make_options([])
74
+ options.password.should be_nil
75
+ end
76
+
77
+ it 'sets the username to what is specified' do
78
+ options = make_options(%w(-P top_secret table))
79
+ options.password.should == 'top_secret'
80
+ end
81
+ end
82
+
83
+ describe 'csv mode is default' do
84
+ it "sets the mode to :csv" do
85
+ options = make_options([])
86
+ options.dump_mode.should == :csv
87
+ end
88
+ end
89
+
90
+ describe 'csv header option' do
91
+ it "the csv_header flag is false by default" do
92
+ options = make_options([])
93
+ options.csv_header.should be_false
94
+ end
95
+
96
+ it "sets the options flag csv_header to true" do
97
+ options = make_options(%w(-H table))
98
+ options.csv_header.should be_true
99
+ end
100
+ end
101
+
102
+ describe 'insert mode option' do
103
+ it "sets the mode to :insert" do
104
+ options = make_options(%w(-i table))
105
+ options.dump_mode.should == :insert
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "sqldump/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "sqldump"
7
+ s.version = Sqldump::VERSION
8
+ s.authors = ["Mats Sigge"]
9
+ s.email = ["mats.sigge@gmail.com"]
10
+ s.homepage = "http://github.com/matssigge/sqldump"
11
+ s.summary = %q{Dumps data as insert statements}
12
+ s.description = %q{A command line tool to generate SQL insert or update statements from the data in a database.}
13
+
14
+ # s.rubyforge_project = "sqldump"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_dependency "dbi"
23
+ s.add_dependency "dbd-sqlite3"
24
+ s.add_development_dependency "rspec"
25
+ s.add_development_dependency "cucumber"
26
+ s.add_development_dependency "guard-rspec"
27
+ s.add_development_dependency "rb-fsevent" if RUBY_PLATFORM =~ /darwin/i
28
+ s.add_development_dependency "dbd-pg"
29
+ end
metadata ADDED
@@ -0,0 +1,171 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sqldump
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mats Sigge
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-05 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dbi
16
+ requirement: &2160361380 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2160361380
25
+ - !ruby/object:Gem::Dependency
26
+ name: dbd-sqlite3
27
+ requirement: &2160360440 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2160360440
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &2160359900 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2160359900
47
+ - !ruby/object:Gem::Dependency
48
+ name: cucumber
49
+ requirement: &2160359280 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2160359280
58
+ - !ruby/object:Gem::Dependency
59
+ name: guard-rspec
60
+ requirement: &2160358300 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2160358300
69
+ - !ruby/object:Gem::Dependency
70
+ name: rb-fsevent
71
+ requirement: &2160357060 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2160357060
80
+ - !ruby/object:Gem::Dependency
81
+ name: dbd-pg
82
+ requirement: &2160353280 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2160353280
91
+ description: A command line tool to generate SQL insert or update statements from
92
+ the data in a database.
93
+ email:
94
+ - mats.sigge@gmail.com
95
+ executables:
96
+ - sqldump
97
+ extensions: []
98
+ extra_rdoc_files: []
99
+ files:
100
+ - .gitignore
101
+ - .idea/inspectionProfiles/Project_Default.xml
102
+ - .idea/inspectionProfiles/profiles_settings.xml
103
+ - .rspec
104
+ - CHANGELOG
105
+ - Gemfile
106
+ - Guardfile
107
+ - LICENSE
108
+ - README.md
109
+ - Rakefile
110
+ - bin/sqldump
111
+ - features/database_types.feature
112
+ - features/dump_as_csv.feature
113
+ - features/dump_as_insert.feature
114
+ - features/step_definitions/sqldump_steps.rb
115
+ - features/support/cuke_table_data_converter.rb
116
+ - features/support/env.rb
117
+ - lib/sqldump.rb
118
+ - lib/sqldump/connector.rb
119
+ - lib/sqldump/csv_formatter.rb
120
+ - lib/sqldump/dumper.rb
121
+ - lib/sqldump/formatter.rb
122
+ - lib/sqldump/insert_formatter.rb
123
+ - lib/sqldump/options.rb
124
+ - lib/sqldump/version.rb
125
+ - numbers
126
+ - spec/spec_helper.rb
127
+ - spec/sqldump/connector_spec.rb
128
+ - spec/sqldump/csv_formatter_spec.rb
129
+ - spec/sqldump/dumper_spec.rb
130
+ - spec/sqldump/formatter_spec.rb
131
+ - spec/sqldump/insert_formatter_spec.rb
132
+ - spec/sqldump/options_spec.rb
133
+ - sqldump.gemspec
134
+ homepage: http://github.com/matssigge/sqldump
135
+ licenses: []
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 1.8.10
155
+ signing_key:
156
+ specification_version: 3
157
+ summary: Dumps data as insert statements
158
+ test_files:
159
+ - features/database_types.feature
160
+ - features/dump_as_csv.feature
161
+ - features/dump_as_insert.feature
162
+ - features/step_definitions/sqldump_steps.rb
163
+ - features/support/cuke_table_data_converter.rb
164
+ - features/support/env.rb
165
+ - spec/spec_helper.rb
166
+ - spec/sqldump/connector_spec.rb
167
+ - spec/sqldump/csv_formatter_spec.rb
168
+ - spec/sqldump/dumper_spec.rb
169
+ - spec/sqldump/formatter_spec.rb
170
+ - spec/sqldump/insert_formatter_spec.rb
171
+ - spec/sqldump/options_spec.rb