rdbms_sampler 1.1.1
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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/Gemfile +6 -0
- data/README.md +89 -0
- data/Rakefile +2 -0
- data/bin/rdbms_sampler +36 -0
- data/lib/rdbms_sampler.rb +5 -0
- data/lib/rdbms_sampler/dependency.rb +35 -0
- data/lib/rdbms_sampler/foreign_key.rb +26 -0
- data/lib/rdbms_sampler/sample.rb +76 -0
- data/lib/rdbms_sampler/table_sample.rb +169 -0
- data/lib/rdbms_sampler/version.rb +3 -0
- data/rdbms_sampler.gemspec +29 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d9834c6b4cba2c6c8271f9e8f744e43d1c8350b3465db252fb85b059ea1c93cd
|
4
|
+
data.tar.gz: feee1ba4541dc3e05eb3697272f0bdb049c5bdfbfa17344ad5b9d58675df5672
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3646c28b02866c51364d89ca2e97b847fe6d1a72bc5904e5085b5eae7267effac3ac185dca8ccda389cdc645359e106324a5a68e5bf3691ac56c9138e2e0116b
|
7
|
+
data.tar.gz: fa0096829e7bae368a20b934284400aa39fc7c87e1840a39a381d7e970e6032a01422a721ba2d9d0b3cc8b17d130ae57c514191dae71a7ab443167f8ff71073e
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
RDBMS Sampler
|
2
|
+
=============
|
3
|
+
|
4
|
+
Command line utility for extracting a sample (subset of all records) from a relational
|
5
|
+
database system (such as MySQL) while *maintaining the referential integrity* of the sample.
|
6
|
+
|
7
|
+
Description
|
8
|
+
-----------
|
9
|
+
|
10
|
+
Need e.g. 1000 rows from each of your production tables, but feel the pain of making
|
11
|
+
sure to include dependent rows, their dependents and so on, ad infinitum?
|
12
|
+
|
13
|
+
Look no further. This tiny utility will take care that referential dependencies are
|
14
|
+
fulfilled by recursively expanding the row sample with unfilled dependencies until
|
15
|
+
the sample is referentially consistent.
|
16
|
+
|
17
|
+
Installation
|
18
|
+
------------
|
19
|
+
|
20
|
+
Install with `gem install rdbms_sampler`.
|
21
|
+
|
22
|
+
Alternatively, clone the repository and install dependencies with `bundle install`.
|
23
|
+
Then execute with `bundle exec rdbms_sampler ...`.
|
24
|
+
|
25
|
+
Commands
|
26
|
+
--------
|
27
|
+
|
28
|
+
help Display global or [command] help documentation.
|
29
|
+
sample Extract a sample from the given connection
|
30
|
+
|
31
|
+
Options
|
32
|
+
-------
|
33
|
+
|
34
|
+
--adapter NAME
|
35
|
+
ActiveRecord adapter to use
|
36
|
+
|
37
|
+
--databases NAMES
|
38
|
+
Comma-separated list of databases to sample
|
39
|
+
|
40
|
+
--username USER
|
41
|
+
Username for connection
|
42
|
+
|
43
|
+
--password PASSWORD
|
44
|
+
Password for connection
|
45
|
+
|
46
|
+
--encoding ENCODING
|
47
|
+
Encoding for connection
|
48
|
+
|
49
|
+
--host HOST
|
50
|
+
Host name or IP for connection
|
51
|
+
|
52
|
+
--socket PATH
|
53
|
+
Socket for connection
|
54
|
+
|
55
|
+
--rows NUM
|
56
|
+
Number of rows to sample per table
|
57
|
+
|
58
|
+
--log PATH
|
59
|
+
Log queries to PATH
|
60
|
+
|
61
|
+
Global Options
|
62
|
+
--------------
|
63
|
+
|
64
|
+
-h, --help
|
65
|
+
Display help documentation
|
66
|
+
|
67
|
+
-v, --version
|
68
|
+
Display version information
|
69
|
+
|
70
|
+
-t, --trace
|
71
|
+
Display backtrace when an error occurs
|
72
|
+
|
73
|
+
Usage
|
74
|
+
-----
|
75
|
+
|
76
|
+
rdbms_sampler --databases DB1,DB2 --username USER --password PASS --rows 100 > sample.sql
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
CAVEATS
|
81
|
+
-------
|
82
|
+
|
83
|
+
Only single-column foreign keys are currently handled.
|
84
|
+
|
85
|
+
Additionally, due to a bug in the current implementation, if a referenced column
|
86
|
+
is named anything but `id`, referenced rows might get included multiple times.
|
87
|
+
|
88
|
+
You will probably need to disable foreign key check *during import*, since inserts in
|
89
|
+
the output are not ordered with respect to referential integrity.
|
data/Rakefile
ADDED
data/bin/rdbms_sampler
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "rdbms_sampler"
|
3
|
+
require "commander/import"
|
4
|
+
require "logger"
|
5
|
+
|
6
|
+
program :version, RdbmsSampler::VERSION
|
7
|
+
program :description, 'Extract a sample of desired size from a database while ensuring referential integrity.'
|
8
|
+
default_command :sample
|
9
|
+
|
10
|
+
command :sample do |c|
|
11
|
+
c.description = 'Extract a sample from the given connection'
|
12
|
+
c.option '--adapter NAME', String, 'ActiveRecord adapter to use'
|
13
|
+
c.option '--databases NAMES', String, 'Comma-separated names of databases to sample'
|
14
|
+
c.option '--username USER', String, 'Username for connection'
|
15
|
+
c.option '--password PASSWORD', String, 'Password for connection'
|
16
|
+
c.option '--encoding ENCODING', String, 'Encoding for connection'
|
17
|
+
c.option '--socket PATH', String, 'Socket for connection'
|
18
|
+
c.option '--host HOST_NAME', String, 'Host name'
|
19
|
+
c.option '--rows NUM', Integer, 'Number of rows to sample per table'
|
20
|
+
c.option '--log PATH', String, 'Log queries to PATH'
|
21
|
+
c.when_called do |args, options|
|
22
|
+
options.default \
|
23
|
+
:adapter => 'mysql2',
|
24
|
+
:username => 'root',
|
25
|
+
:encoding => 'utf8',
|
26
|
+
:cast => false,
|
27
|
+
:rows => 1000
|
28
|
+
ActiveRecord::Base.logger = Logger.new(options.log) if options.log
|
29
|
+
warn 'Connecting...'
|
30
|
+
ActiveRecord::Base.establish_connection(options.__hash__).with_connection do |conn|
|
31
|
+
schemas = options.databases.split(/,/)
|
32
|
+
puts RdbmsSampler::Sample.new(conn: conn, rows_per_table: options.rows, schemas: schemas).to_sql
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RdbmsSampler
|
2
|
+
class Dependency
|
3
|
+
|
4
|
+
attr_reader :parent_schema
|
5
|
+
attr_reader :parent_table
|
6
|
+
attr_reader :parent_key
|
7
|
+
attr_reader :child_schema_name
|
8
|
+
attr_reader :child_table_name
|
9
|
+
attr_reader :child_key
|
10
|
+
attr_reader :value
|
11
|
+
|
12
|
+
def initialize(parent_schema, parent_table, parent_key, child_schema, child_table, child_key, value)
|
13
|
+
@parent_schema = parent_schema
|
14
|
+
@parent_table = parent_table
|
15
|
+
@parent_key = parent_key
|
16
|
+
@child_schema_name = child_schema
|
17
|
+
@child_table_name = child_table
|
18
|
+
@child_key = child_key
|
19
|
+
@value = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def identifier
|
23
|
+
"#{child_schema_name}.#{child_table_name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def eql? other
|
27
|
+
identifier == other.identifier and child_key == other.child_key and value == other.value
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"reference from #{parent_schema}.#{parent_table}.#{parent_key} to #{identifier}[#{child_key}=#{value}]"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RdbmsSampler
|
2
|
+
|
3
|
+
class ForeignKey
|
4
|
+
attr_reader :constraint_name
|
5
|
+
attr_reader :schema
|
6
|
+
attr_reader :table
|
7
|
+
attr_reader :key
|
8
|
+
attr_reader :referenced_schema
|
9
|
+
attr_reader :referenced_table
|
10
|
+
attr_reader :referenced_key
|
11
|
+
|
12
|
+
def initialize(constraint_name, schema, table, key,
|
13
|
+
referenced_schema, referenced_table, referenced_key)
|
14
|
+
|
15
|
+
@constraint_name = constraint_name
|
16
|
+
@schema = schema
|
17
|
+
@table = table
|
18
|
+
@key = key
|
19
|
+
@referenced_schema = referenced_schema
|
20
|
+
@referenced_table = referenced_table
|
21
|
+
@referenced_key = referenced_key
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'rdbms_sampler/table_sample'
|
2
|
+
require 'active_support/core_ext/array'
|
3
|
+
|
4
|
+
module RdbmsSampler
|
5
|
+
|
6
|
+
class Sample
|
7
|
+
|
8
|
+
def initialize(options ={})
|
9
|
+
@connection = options[:conn]
|
10
|
+
@rows_per_table = options[:rows_per_table] || 1000
|
11
|
+
@table_samples = {}
|
12
|
+
@schemas = options[:schemas]
|
13
|
+
@computed = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def compute!
|
17
|
+
quoted_schema_names = @schemas.collect do |name|
|
18
|
+
@connection.quote_table_name(name)
|
19
|
+
end
|
20
|
+
warn "Discovering tables in databases: #{quoted_schema_names.to_sentence}..."
|
21
|
+
tables_without_views.each do |schema_name, table_name|
|
22
|
+
table_sample = TableSample.new(@connection, schema_name, table_name, @rows_per_table)
|
23
|
+
@table_samples[table_sample.identifier] = table_sample
|
24
|
+
end
|
25
|
+
return warn 'No tables found!' unless @table_samples.count > 0
|
26
|
+
warn "Sampling #{@table_samples.count} tables..."
|
27
|
+
@table_samples.values.map &:sample!
|
28
|
+
warn 'Ensuring referential integrity...'
|
29
|
+
begin
|
30
|
+
new_dependencies = 0
|
31
|
+
@table_samples.values.each do |table_sample|
|
32
|
+
newly_added = table_sample.ensure_referential_integrity(self)
|
33
|
+
if newly_added > 0
|
34
|
+
new_dependencies += newly_added
|
35
|
+
warn " Expanded sample with #{newly_added} new rows referenced from table #{table_sample.quoted_name}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
warn " Discovered #{new_dependencies} new dependencies" if new_dependencies > 0
|
39
|
+
end while new_dependencies > 0
|
40
|
+
warn 'Referential integrity obtained'
|
41
|
+
|
42
|
+
warn 'Final sample contains:'
|
43
|
+
@table_samples.values.each do |table_sample|
|
44
|
+
warn " #{table_sample.size} row(s) from `#{table_sample.identifier}`"
|
45
|
+
end
|
46
|
+
@computed = true
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param [Dependency]
|
50
|
+
# @return [TableSample]
|
51
|
+
def table_sample_for_dependency(dependency)
|
52
|
+
raise "Table sample for [#{dependency.identifier}] not found" unless @table_samples.include? dependency.identifier
|
53
|
+
@table_samples[dependency.identifier]
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_sql
|
57
|
+
compute! unless @computed
|
58
|
+
@table_samples.values.collect(&:to_sql) * "\n"
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def tables_without_views
|
64
|
+
quoted_schema_names = @schemas.collect { |name|
|
65
|
+
@connection.quote(name)
|
66
|
+
}.join(', ')
|
67
|
+
@connection.execute <<SQL
|
68
|
+
SELECT TABLE_SCHEMA, TABLE_NAME
|
69
|
+
FROM INFORMATION_SCHEMA.TABLES
|
70
|
+
WHERE TABLE_TYPE = 'BASE TABLE'
|
71
|
+
AND TABLE_SCHEMA IN (#{quoted_schema_names})
|
72
|
+
SQL
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'rdbms_sampler/dependency'
|
3
|
+
require 'rdbms_sampler/foreign_key'
|
4
|
+
|
5
|
+
module RdbmsSampler
|
6
|
+
|
7
|
+
class TableSample
|
8
|
+
|
9
|
+
attr_reader :pending_dependencies
|
10
|
+
|
11
|
+
def initialize(connection, schema_name, table_name, size = 1000)
|
12
|
+
@schema = schema_name
|
13
|
+
@table = table_name
|
14
|
+
@connection = connection
|
15
|
+
@size = size
|
16
|
+
@pending_dependencies = Set.new
|
17
|
+
@sample = Set.new
|
18
|
+
@sampled = false
|
19
|
+
@sampled_ids = Set.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def sample!
|
23
|
+
fetch(@size) unless @sampled
|
24
|
+
@sample
|
25
|
+
end
|
26
|
+
|
27
|
+
def size
|
28
|
+
@sampled ? @sample.size : @size
|
29
|
+
end
|
30
|
+
|
31
|
+
def identifier
|
32
|
+
"#{@schema}.#{@table}"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add the given dependency to the sample
|
36
|
+
# @param [Dependency] dependency
|
37
|
+
def fulfil(dependency)
|
38
|
+
return 0 if fulfilled?(dependency)
|
39
|
+
quoted_column = @connection.quote_column_name dependency.child_key
|
40
|
+
quoted_value = @connection.quote dependency.value
|
41
|
+
sql = "SELECT * FROM #{quoted_name} WHERE #{quoted_column} = #{quoted_value}"
|
42
|
+
row = @connection.select_one(sql)
|
43
|
+
raise "Could not fulfil #{dependency} using query [#{sql}]" if row.nil?
|
44
|
+
add row
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param [Dependency] dependency
|
48
|
+
def fulfilled?(dependency)
|
49
|
+
# FIXME: Only handles `id` column
|
50
|
+
return false if dependency.child_key != 'id'
|
51
|
+
|
52
|
+
@sampled_ids.include?(dependency.value)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Add a row to the table sample.
|
56
|
+
# Returns number of new dependencies introduced.
|
57
|
+
def add(row)
|
58
|
+
return 0 unless @sample.add? row
|
59
|
+
@sampled_ids.add row['id'] if row['id']
|
60
|
+
dependencies_for(row).collect { |dep|
|
61
|
+
1 if @pending_dependencies.add?(dep)
|
62
|
+
}.compact.sum
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param [Sample] sample
|
66
|
+
def ensure_referential_integrity(sample)
|
67
|
+
dependencies_in_progress = @pending_dependencies
|
68
|
+
@pending_dependencies = Set.new
|
69
|
+
dependencies_in_progress.map { |dependency|
|
70
|
+
dependency_sample = sample.table_sample_for_dependency(dependency)
|
71
|
+
dependency_sample.fulfil(dependency)
|
72
|
+
}.compact.sum
|
73
|
+
end
|
74
|
+
|
75
|
+
def to_sql
|
76
|
+
ret = "\n-- Sample from #{quoted_name} (#{@sample.count} rows)\n"
|
77
|
+
unless @sample.empty?
|
78
|
+
quoted_cols = @sample.first.keys.collect { |col| @connection.quote_column_name col }
|
79
|
+
# INSERT in batches to reduce the likelihood of hitting `max_allowed_packet`
|
80
|
+
@sample.each_slice(250) do |rows|
|
81
|
+
values = rows.collect { |row|
|
82
|
+
row.values.map { |val|
|
83
|
+
@connection.quote(val)
|
84
|
+
} * ','
|
85
|
+
} * "),\n ("
|
86
|
+
ret << "INSERT INTO #{quoted_name} \n (#{quoted_cols * ','}) \nVALUES \n (#{values});\n"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
ret
|
90
|
+
end
|
91
|
+
|
92
|
+
def quoted_name
|
93
|
+
@connection.quote_table_name(@schema)+'.'+@connection.quote_table_name(@table)
|
94
|
+
end
|
95
|
+
|
96
|
+
protected
|
97
|
+
|
98
|
+
def fetch(count = 1000)
|
99
|
+
sql = "SELECT * FROM #{quoted_name}"
|
100
|
+
unless (pks = self.primary_keys).count == 0
|
101
|
+
order_by = @connection.quote_column_name(pks.first)
|
102
|
+
sql += " ORDER BY #{order_by} DESC"
|
103
|
+
end
|
104
|
+
sql += " LIMIT #{count}"
|
105
|
+
warn " Sampling #{count} rows from #{quoted_name}..."
|
106
|
+
@connection.select_all(sql).each { |row| add(row) }
|
107
|
+
@sampled = true
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param [ForeignKey] fk
|
111
|
+
# @param [Array] row
|
112
|
+
def dependency_for(fk, row)
|
113
|
+
unless (value = row[fk.key]).nil?
|
114
|
+
Dependency.new(fk.schema, fk.table, fk.key, fk.referenced_schema, fk.referenced_table, fk.referenced_key, value)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param [Array] row
|
119
|
+
def dependencies_for(row)
|
120
|
+
foreign_keys.collect { |fk| dependency_for(fk, row) }.compact
|
121
|
+
end
|
122
|
+
|
123
|
+
def foreign_keys
|
124
|
+
@fks ||= discover_foreign_keys
|
125
|
+
end
|
126
|
+
|
127
|
+
def discover_foreign_keys
|
128
|
+
quoted_schema = @connection.quote @schema
|
129
|
+
quoted_table = @connection.quote @table
|
130
|
+
|
131
|
+
sql = <<SQL
|
132
|
+
SELECT
|
133
|
+
fk.constraint_name,
|
134
|
+
fk.table_schema,
|
135
|
+
fk.table_name,
|
136
|
+
fk.column_name,
|
137
|
+
fk.referenced_table_schema,
|
138
|
+
fk.referenced_table_name,
|
139
|
+
fk.referenced_column_name
|
140
|
+
FROM information_schema.key_column_usage fk
|
141
|
+
WHERE fk.referenced_column_name IS NOT NULL
|
142
|
+
AND fk.table_schema = #{quoted_schema}
|
143
|
+
AND fk.table_name = #{quoted_table}
|
144
|
+
SQL
|
145
|
+
|
146
|
+
@connection.execute(sql).map do |row|
|
147
|
+
ForeignKey.new(*row)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def primary_keys
|
152
|
+
quoted_schema = @connection.quote @schema
|
153
|
+
quoted_table = @connection.quote @table
|
154
|
+
|
155
|
+
sql = <<SQL
|
156
|
+
SELECT column_name
|
157
|
+
FROM information_schema.key_column_usage
|
158
|
+
WHERE constraint_name = 'PRIMARY'
|
159
|
+
AND table_schema = #{quoted_schema}
|
160
|
+
AND table_name = #{quoted_table}
|
161
|
+
SQL
|
162
|
+
|
163
|
+
@connection.execute(sql).map do |row|
|
164
|
+
row.first
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "rdbms_sampler/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "rdbms_sampler"
|
6
|
+
s.version = RdbmsSampler::VERSION
|
7
|
+
s.licenses = ['MIT']
|
8
|
+
s.authors = ["Christian Rishoj"]
|
9
|
+
s.email = ["christian@rishoj.net"]
|
10
|
+
s.homepage = "https://github.com/crishoj/rdbms_sampler"
|
11
|
+
s.summary = %q{Extract a sample of records from a database while maintaining referential integrity.}
|
12
|
+
s.description = %q{Ever found yourself wanting a modest amount of fresh rows from a production database for development purposes, but
|
13
|
+
put back by the need to maintain referential integrity in the extracted data sample? This data sampler utility will
|
14
|
+
take care that referential dependencies are fulfilled by recursively fetching any rows referred to by the sample.}
|
15
|
+
|
16
|
+
s.rubyforge_project = "rdbms_sampler"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split("\n")
|
19
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
20
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_dependency "schema_plus_foreign_keys", '~> 0.1', '>= 0.1.7'
|
24
|
+
s.add_dependency "activerecord", '~> 5.0'
|
25
|
+
s.add_dependency "commander", '~> 4.4'
|
26
|
+
s.add_dependency "mysql2", '~> 0.4', '>= 0.4.4'
|
27
|
+
|
28
|
+
s.add_development_dependency "pry", '~> 0.9'
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rdbms_sampler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christian Rishoj
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: schema_plus_foreign_keys
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.1.7
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.1.7
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: activerecord
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '5.0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '5.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: commander
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '4.4'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '4.4'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: mysql2
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0.4'
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 0.4.4
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0.4'
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 0.4.4
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: pry
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0.9'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0.9'
|
95
|
+
description: |-
|
96
|
+
Ever found yourself wanting a modest amount of fresh rows from a production database for development purposes, but
|
97
|
+
put back by the need to maintain referential integrity in the extracted data sample? This data sampler utility will
|
98
|
+
take care that referential dependencies are fulfilled by recursively fetching any rows referred to by the sample.
|
99
|
+
email:
|
100
|
+
- christian@rishoj.net
|
101
|
+
executables:
|
102
|
+
- rdbms_sampler
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- Gemfile
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- bin/rdbms_sampler
|
111
|
+
- lib/rdbms_sampler.rb
|
112
|
+
- lib/rdbms_sampler/dependency.rb
|
113
|
+
- lib/rdbms_sampler/foreign_key.rb
|
114
|
+
- lib/rdbms_sampler/sample.rb
|
115
|
+
- lib/rdbms_sampler/table_sample.rb
|
116
|
+
- lib/rdbms_sampler/version.rb
|
117
|
+
- rdbms_sampler.gemspec
|
118
|
+
homepage: https://github.com/crishoj/rdbms_sampler
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project: rdbms_sampler
|
138
|
+
rubygems_version: 2.7.6
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: Extract a sample of records from a database while maintaining referential
|
142
|
+
integrity.
|
143
|
+
test_files: []
|