sqlload 0.3.0beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/sqlload +109 -0
- data/lib/sqlload.rb +127 -0
- data/lib/sqlload/database.rb +52 -0
- metadata +66 -0
data/bin/sqlload
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2012 Ludovico Fischer
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
require 'sqlload'
|
17
|
+
|
18
|
+
|
19
|
+
module Tasks
|
20
|
+
BASE_DIRECTORY = 'SQL'
|
21
|
+
def self.get_datasets()
|
22
|
+
datasets = []
|
23
|
+
Dir[File.join(BASE_DIRECTORY, '*')].each do |d|
|
24
|
+
if File.directory? d
|
25
|
+
datasets << DataSet.new(d)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
datasets
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.list_available_datasets()
|
32
|
+
self.get_datasets().each do |d|
|
33
|
+
puts File.basename(d.directory)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.load_dataset(dataset_name, user_options)
|
38
|
+
if dataset_name.nil? then abort 'You must specify a dataset to load' end
|
39
|
+
dataset = DataSet.new(File.join(BASE_DIRECTORY, dataset_name), user_options)
|
40
|
+
dataset.load
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.delete_dataset(dataset_name, user_options)
|
44
|
+
if dataset_name.nil? then abort 'You must specify a dataset to reset' end
|
45
|
+
dataset = DataSet.new(File.join(BASE_DIRECTORY, dataset_name), user_options)
|
46
|
+
dataset.delete
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.reset_dataset(dataset_name, user_options)
|
50
|
+
if dataset_name.nil? then abort 'You must specify a dataset to reset' end
|
51
|
+
dataset = DataSet.new(File.join(BASE_DIRECTORY, dataset_name), user_options)
|
52
|
+
dataset.reset
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module User
|
57
|
+
def self.get_commandline_options(command_string)
|
58
|
+
require 'optparse'
|
59
|
+
|
60
|
+
options = {}
|
61
|
+
|
62
|
+
optparse = OptionParser.new do |opts|
|
63
|
+
opts.banner = 'Usage: sqlload [options] list|load|delete|reset [dataset]'
|
64
|
+
|
65
|
+
opts.version = '0.2.1'
|
66
|
+
|
67
|
+
opts.on('-U', '--user USERNAME', 'Specify username') do |user|
|
68
|
+
options[:user] = user
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.on('-W', '--password PASSWORD', 'Specify password') do |password|
|
72
|
+
options[:password] = password
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on('-d', '--database DBNAME', 'Specify database name') do |dbname|
|
76
|
+
options[:dbname] = dbname
|
77
|
+
end
|
78
|
+
|
79
|
+
opts.on('-p', '--port PORT', 'Specify port') do |port|
|
80
|
+
options[:port] = port
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.on_tail('-h', '--help', 'Displays this message') do
|
84
|
+
puts opts
|
85
|
+
exit
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
optparse.parse!(command_string)
|
90
|
+
options
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
user_options = User.get_commandline_options(ARGV)
|
96
|
+
dataset_name = ARGV[1]
|
97
|
+
|
98
|
+
case ARGV[0]
|
99
|
+
when 'list'
|
100
|
+
Tasks.list_available_datasets
|
101
|
+
when 'load'
|
102
|
+
Tasks.load_dataset(dataset_name, user_options)
|
103
|
+
when 'delete'
|
104
|
+
Tasks.delete_dataset(dataset_name, user_options)
|
105
|
+
when 'reset'
|
106
|
+
Tasks.reset_dataset(dataset_name, user_options)
|
107
|
+
|
108
|
+
else abort 'You must specify one of list, load, delete or reset.'
|
109
|
+
end
|
data/lib/sqlload.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# Copyright 2012 Ludovico Fischer
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'sqlload/database'
|
16
|
+
require 'pty'
|
17
|
+
require 'expect'
|
18
|
+
|
19
|
+
##
|
20
|
+
# A collection of data that can be inserted together into a database
|
21
|
+
class DataSet
|
22
|
+
include DBConfig, DB
|
23
|
+
attr_accessor :ups, :downs, :raws, :config, :directory
|
24
|
+
|
25
|
+
##
|
26
|
+
# Creates a dataset using the SQL and the configuration contained
|
27
|
+
# in directory. The supplied user configuration overrides
|
28
|
+
# the configuration contained in the directory
|
29
|
+
def initialize(directory, user_config = {})
|
30
|
+
@ups = []
|
31
|
+
@downs = []
|
32
|
+
@raws = []
|
33
|
+
@directory = directory
|
34
|
+
populate()
|
35
|
+
@config = load_config(directory).merge(user_config)
|
36
|
+
if insufficient(config)
|
37
|
+
raise ArgumentError, 'No database name'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# If there are reset scripts available,
|
43
|
+
# executes them before executing once again the regular scripts
|
44
|
+
def reset
|
45
|
+
if @downs.empty?
|
46
|
+
raise ArgumentError, 'There are no reset scripts to run'
|
47
|
+
end
|
48
|
+
delete
|
49
|
+
load
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Executes the SQL scripts associated with the dataset.
|
54
|
+
def load
|
55
|
+
get_db_connection(@config) do |db_connection|
|
56
|
+
@ups.each do |s|
|
57
|
+
result = db_connection.exec(s.statement)
|
58
|
+
puts "#{s.filename}: #{result.cmd_status}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
psql_count = 0
|
62
|
+
@raws.each do |s|
|
63
|
+
system({'PGPASSWORD' => @config[:password]}, "psql -h localhost -U #{@config[:user]} -d #{@config[:dbname]} < #{s.filename}")
|
64
|
+
psql_count += 1
|
65
|
+
end
|
66
|
+
puts "psql invoked #{psql_count} time(s)"
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Executes the SQL scripts marked as reset scripts
|
71
|
+
def delete
|
72
|
+
get_db_connection(@config) do |db_connection|
|
73
|
+
@downs.each do |s|
|
74
|
+
result = db_connection.exec(s.statement)
|
75
|
+
puts "#{s.filename}: #{result.cmd_status}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_s
|
81
|
+
File.basename(@directory)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
DataPiece = Struct.new(:filename, :statement)
|
87
|
+
|
88
|
+
# Fills the list of statements to execute from the information
|
89
|
+
# found by inspecting the directory contents
|
90
|
+
def populate()
|
91
|
+
require 'find'
|
92
|
+
Find.find(@directory) do |path|
|
93
|
+
if File.file?(path) && File.extname(path) == '.sql'
|
94
|
+
data = DataPiece.new(path, File.read(path))
|
95
|
+
if is_raws path
|
96
|
+
@raws << data
|
97
|
+
elsif is_downs path
|
98
|
+
@downs << data
|
99
|
+
elsif is_ups path
|
100
|
+
@ups << data
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
@ups.sort! do |x, y|
|
106
|
+
File.basename(x.filename) <=> File.basename(y.filename)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns true if the configuration does not contain enough information
|
111
|
+
# to connect to a database
|
112
|
+
def insufficient(config)
|
113
|
+
config[:dbname].nil?
|
114
|
+
end
|
115
|
+
|
116
|
+
def is_ups(path)
|
117
|
+
File.extname(path) == '.sql' && File.basename(path) != 'reset.sql'
|
118
|
+
end
|
119
|
+
|
120
|
+
def is_downs(path)
|
121
|
+
File.basename(path) == 'reset.sql'
|
122
|
+
end
|
123
|
+
|
124
|
+
def is_raws(path)
|
125
|
+
File.fnmatch?('*psql*', path)
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Copyright 2012 Ludovico Fischer
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module DB
|
16
|
+
require 'pg'
|
17
|
+
##
|
18
|
+
# Yields a database connection configured with the supplied options.
|
19
|
+
# This method will attempt to close the connection once the block terminates.
|
20
|
+
def get_db_connection(options)
|
21
|
+
dbname = options[:dbname]
|
22
|
+
unless dbname
|
23
|
+
abort "You must specify a database name"
|
24
|
+
end
|
25
|
+
user = options.fetch(:user, dbname)
|
26
|
+
password = options[:password]
|
27
|
+
port = options.fetch(:port, 5432)
|
28
|
+
begin
|
29
|
+
db_connection = PG.connect(:host => 'localhost', :port => port, :dbname => dbname, :user => user, :password => password)
|
30
|
+
yield db_connection
|
31
|
+
ensure
|
32
|
+
db_connection.finish unless db_connection.nil?
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
module DBConfig
|
39
|
+
##
|
40
|
+
# Generates a configuration hash from a JSON file
|
41
|
+
#
|
42
|
+
# [path] the path to the dataset to load a config for
|
43
|
+
def load_config(path)
|
44
|
+
require 'json'
|
45
|
+
|
46
|
+
config = JSON.parse(File.read(self.get_config_path(path)), :symbolize_names => true)
|
47
|
+
end
|
48
|
+
|
49
|
+
def get_config_path(dataset_path)
|
50
|
+
File.join(dataset_path, 'config.json')
|
51
|
+
end
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sqlload
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0beta
|
5
|
+
prerelease: 5
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ludovico Fischer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: pg
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.13.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.13.0
|
30
|
+
description: ! 'sqlload inspects a directory hierarchy: for each subdirectory, sqlload
|
31
|
+
offers to execute any SQL file, using the database specified in the JSON configuration.'
|
32
|
+
email: ludovico.fischer@lunatech.com
|
33
|
+
executables:
|
34
|
+
- sqlload
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- lib/sqlload.rb
|
39
|
+
- lib/sqlload/database.rb
|
40
|
+
- bin/sqlload
|
41
|
+
homepage: https://github.com/ludovicofischer/sqlloader
|
42
|
+
licenses:
|
43
|
+
- Apache License, Version 2.0
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>'
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 1.3.1
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.8.23
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: Load and execute SQL from a directory hierarchy. Only supports PostgreSQL
|
66
|
+
test_files: []
|