sneaql 0.0.8-java
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/bin/sneaql +273 -0
- data/lib/sneaql.rb +284 -0
- data/lib/sneaql_lib/base.rb +224 -0
- data/lib/sneaql_lib/core.rb +346 -0
- data/lib/sneaql_lib/database_manager.rb +79 -0
- data/lib/sneaql_lib/database_prefs/redshift.rb +22 -0
- data/lib/sneaql_lib/database_prefs/sqlite.rb +12 -0
- data/lib/sneaql_lib/database_prefs/vertica.rb +21 -0
- data/lib/sneaql_lib/exceptions.rb +78 -0
- data/lib/sneaql_lib/expressions.rb +238 -0
- data/lib/sneaql_lib/lock_manager.rb +176 -0
- data/lib/sneaql_lib/parser.rb +89 -0
- data/lib/sneaql_lib/recordset.rb +97 -0
- data/lib/sneaql_lib/repo_manager.rb +95 -0
- data/lib/sneaql_lib/standard.rb +30 -0
- data/lib/sneaql_lib/standard_db_objects.rb +232 -0
- data/lib/sneaql_lib/step_manager.rb +60 -0
- metadata +131 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'zip/zip'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
#top level namespace for sneaql objects
|
6
|
+
module Sneaql
|
7
|
+
# contains the base classes for the extendable parts of sneaql:
|
8
|
+
# commands (the actual commands specified in sneaql tags)
|
9
|
+
# repo_managers (used to pull the sql files from a remote or local source)
|
10
|
+
# metadata managers (to get information about each step)
|
11
|
+
# in addition to the base classes, the mapped class/class_map
|
12
|
+
# utilities are used to provide a dynamic class system
|
13
|
+
# this class system allows you to register a class under a type
|
14
|
+
# you can later look up the class by type and a text identifier
|
15
|
+
# then instantiate a new instance from there
|
16
|
+
module Core
|
17
|
+
# global map of all classes
|
18
|
+
@@class_map = {}
|
19
|
+
|
20
|
+
# allows external access to class_map for testing purposes
|
21
|
+
def self.class_map
|
22
|
+
@@class_map
|
23
|
+
end
|
24
|
+
|
25
|
+
# adds a new class to the map
|
26
|
+
# @param [Symbol] type class type (user settable, can be :command, :repo_manager, etc)
|
27
|
+
# @param [Hash] mapped_class_hash in the format { text: text, mapped_class: mapped_class }
|
28
|
+
def self.add_mapped_class(type, mapped_class_hash)
|
29
|
+
@@class_map[type] == [] unless @@class_map.keys.include?(type)
|
30
|
+
@@class_map[type] << mapped_class_hash
|
31
|
+
end
|
32
|
+
|
33
|
+
# makes sure that the type exists before appending the class information
|
34
|
+
# @param [Symbol] type class type (user settable, can be :command, :repo_manager, etc)
|
35
|
+
def self.insure_type_exists(type)
|
36
|
+
@@class_map[type] = [] unless @@class_map.key?(type)
|
37
|
+
end
|
38
|
+
|
39
|
+
# returns the class referenced by the type/text combination
|
40
|
+
# @param [Symbol] type class type (user settable, can be :command, :repo_manager, etc)
|
41
|
+
# @param [String] text to when searching within this type
|
42
|
+
# @return [Class] returns the class you are searching for
|
43
|
+
def self.find_class(type, text)
|
44
|
+
@@class_map[type].each do |t|
|
45
|
+
return t[:mapped_class] if t[:text] == text
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Handles registration of a class to the class_map
|
50
|
+
# Ignores duplicate definitions if they occur
|
51
|
+
class RegisterMappedClass
|
52
|
+
# Registers the class into the class_map.
|
53
|
+
# @param [Symbol] type class type (user settable, can be :command, :repo_manager, etc)
|
54
|
+
# @param [String] text to when searching within this type
|
55
|
+
# @param [Class] mapped_class class to be returned when search matches type and text
|
56
|
+
def initialize(type, text, mapped_class)
|
57
|
+
Sneaql::Core.insure_type_exists(type)
|
58
|
+
# check to see if the reference text is already being used by this type
|
59
|
+
unless Sneaql::Core.class_map[type].map { |c| c[:text] }.include?(text)
|
60
|
+
Sneaql::Core.add_mapped_class(
|
61
|
+
type,
|
62
|
+
{ text: text, mapped_class: mapped_class }
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Base class for SneaQL command tags
|
69
|
+
class SneaqlCommand
|
70
|
+
# this is the base object for a sneaql command
|
71
|
+
# subclass this and override the action method
|
72
|
+
# @param [Object] jdbc_connection JDBC connection object to database
|
73
|
+
# @param [Sneaql::Core::ExpressionHandler] expression_handler
|
74
|
+
# @param [Sneaql::Core::RecordsetManager] recordset_manager
|
75
|
+
# @param [String] statement SQL statement provided in body, with all variables resolved
|
76
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
77
|
+
def initialize(jdbc_connection, expression_handler, recordset_manager, statement, logger = nil)
|
78
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
79
|
+
|
80
|
+
@jdbc_connection = jdbc_connection
|
81
|
+
@expression_handler = expression_handler
|
82
|
+
@statement = statement
|
83
|
+
@recordset_manager = recordset_manager
|
84
|
+
end
|
85
|
+
|
86
|
+
# override this method with the actual code for your command
|
87
|
+
def action
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
# override with an array in the form [:expression, :operator]
|
92
|
+
def arg_definition
|
93
|
+
[]
|
94
|
+
end
|
95
|
+
|
96
|
+
# override this if you have a complex tag structure
|
97
|
+
# @param [Array] args argument array to validate
|
98
|
+
# @return [Boolean] true if all arguments are valid
|
99
|
+
def validate_args(args)
|
100
|
+
return false if args.length != arg_definition.length
|
101
|
+
return true if (arg_definition == []) and (args == [])
|
102
|
+
valid = []
|
103
|
+
args.each_with_index do |a, i|
|
104
|
+
case
|
105
|
+
when arg_definition[i] == :variable then
|
106
|
+
valid << valid_variable?(a)
|
107
|
+
when arg_definition[i] == :expression then
|
108
|
+
valid << valid_expression?(a)
|
109
|
+
when arg_definition[i] == :operator then
|
110
|
+
valid << valid_operator?(a)
|
111
|
+
when arg_definition[i] == :recordset then
|
112
|
+
valid << valid_recordset?(a)
|
113
|
+
else valid << false end
|
114
|
+
end
|
115
|
+
@logger.debug(valid)
|
116
|
+
!valid.include?(false)
|
117
|
+
end
|
118
|
+
|
119
|
+
# validates that the value is a valid variable name
|
120
|
+
# @param [String] a value to test
|
121
|
+
# @return [Boolean]
|
122
|
+
def valid_variable?(a)
|
123
|
+
@expression_handler.valid_session_variable_name?(a.to_s.strip)
|
124
|
+
end
|
125
|
+
|
126
|
+
# validates that the value is a valid expression
|
127
|
+
# @param [String, Float, Fixnum] a value to test
|
128
|
+
# @return [Boolean]
|
129
|
+
def valid_expression?(a)
|
130
|
+
@expression_handler.valid_expression_reference?(a.to_s.strip)
|
131
|
+
end
|
132
|
+
|
133
|
+
# validates that the value is a valid operator
|
134
|
+
# @param [String] a value to test
|
135
|
+
# @return [Boolean]
|
136
|
+
def valid_operator?(a)
|
137
|
+
@expression_handler.valid_operators.include?(a.to_s.strip)
|
138
|
+
end
|
139
|
+
|
140
|
+
# validates that the value is a valid recordset name
|
141
|
+
# @param [String] a value to test
|
142
|
+
# @return [Boolean]
|
143
|
+
def valid_recordset?(a)
|
144
|
+
@recordset_manager.valid_recordset_name?(a.to_s.strip)
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
# these are set during initialize
|
150
|
+
# reference to jdbc connection object
|
151
|
+
attr_accessor :jdbc_connection
|
152
|
+
|
153
|
+
# reference to expression handler object for this transform
|
154
|
+
attr_accessor :expression_handler
|
155
|
+
|
156
|
+
# actual sql statement with all variables dereferenced
|
157
|
+
attr_accessor :statement
|
158
|
+
end # class
|
159
|
+
|
160
|
+
# base class for repo managers
|
161
|
+
class RepoDownloadManager
|
162
|
+
# this is the directory that the repo operates in
|
163
|
+
attr_reader :repo_base_dir
|
164
|
+
|
165
|
+
# @param [Hash] params parameters passed to transform will be passed here
|
166
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
167
|
+
def initialize(params, logger = nil)
|
168
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
169
|
+
|
170
|
+
@repo_base_dir = "#{params[:repo_base_dir]}/#{params[:transform_name]}"
|
171
|
+
@params = params
|
172
|
+
|
173
|
+
# perform the actual actions of managing the repo
|
174
|
+
manage_repo
|
175
|
+
end
|
176
|
+
|
177
|
+
# method to drop and rebuild the specified directory
|
178
|
+
# all files and subdirectories will be destroyed
|
179
|
+
# @param [String] directory
|
180
|
+
def drop_and_rebuild_directory(directory)
|
181
|
+
@logger.info("dropping and recreating repo directory #{directory}")
|
182
|
+
FileUtils.remove_dir(directory) if Dir.exist?(directory)
|
183
|
+
FileUtils.mkdir_p(directory)
|
184
|
+
end
|
185
|
+
|
186
|
+
# override in your implementation
|
187
|
+
def manage_repo
|
188
|
+
nil
|
189
|
+
end
|
190
|
+
|
191
|
+
# copied this from the internet
|
192
|
+
# http://www.markhneedham.com/blog/2008/10/02/ruby-unzipping-a-file-using-rubyzip/
|
193
|
+
def unzip_file(file, destination)
|
194
|
+
::Zip::ZipFile.open(file) do |zip_file|
|
195
|
+
zip_file.each do |f|
|
196
|
+
f_path = File.join(destination, f.name)
|
197
|
+
FileUtils.mkdir_p(File.dirname(f_path))
|
198
|
+
zip_file.extract(f, f_path) unless File.exist?(f_path)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# abstracted to allow this metadata to come from any source
|
205
|
+
class StepMetadataManager
|
206
|
+
# value should be array of metadata hashes in the form `{ step_number: j['step_number'], step_file: j['step_file'] }`
|
207
|
+
attr_reader :steps
|
208
|
+
|
209
|
+
# @param [Hash] params parameters passed to transform will be passed here
|
210
|
+
# @param [Logger] logger object otherwise will default to new Logger
|
211
|
+
def initialize(params, logger = nil)
|
212
|
+
@logger = logger ? logger : Logger.new(STDOUT)
|
213
|
+
@params = params
|
214
|
+
manage_steps
|
215
|
+
end
|
216
|
+
|
217
|
+
# override with a method that will override steps with an array of
|
218
|
+
# steps in the format :step_number, :step_file
|
219
|
+
def manage_steps
|
220
|
+
nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,346 @@
|
|
1
|
+
require 'jdbc_helpers'
|
2
|
+
require_relative 'base.rb'
|
3
|
+
require_relative 'exceptions.rb'
|
4
|
+
|
5
|
+
module Sneaql
|
6
|
+
module Core
|
7
|
+
# Core Sneaql language command tags.
|
8
|
+
# You can create your own tags by extending
|
9
|
+
# Sneaql::Core::SneaqlCommand and overriding the
|
10
|
+
# action method. You should also override arg_definition
|
11
|
+
# and potentially validate_args if have a complex argument
|
12
|
+
# structure.
|
13
|
+
module Commands
|
14
|
+
# assigns a session variable to a provided value
|
15
|
+
class SneaqlAssign < Sneaql::Core::SneaqlCommand
|
16
|
+
Sneaql::Core::RegisterMappedClass.new(
|
17
|
+
:command,
|
18
|
+
'assign',
|
19
|
+
Sneaql::Core::Commands::SneaqlAssign
|
20
|
+
)
|
21
|
+
|
22
|
+
# @param [String] var_name
|
23
|
+
# @param [String] value expression (must be a string)
|
24
|
+
def action(var_name, value)
|
25
|
+
@expression_handler.set_session_variable(var_name, value)
|
26
|
+
end
|
27
|
+
|
28
|
+
# argument types
|
29
|
+
def arg_definition
|
30
|
+
[:variable, :expression]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# assigns a session variable to a value returned from a sql query
|
35
|
+
class SneaqlAssignResult < Sneaql::Core::SneaqlCommand
|
36
|
+
Sneaql::Core::RegisterMappedClass.new(
|
37
|
+
:command,
|
38
|
+
'assign_result',
|
39
|
+
Sneaql::Core::Commands::SneaqlAssignResult
|
40
|
+
)
|
41
|
+
|
42
|
+
# run the query... then assign the result to a session variable
|
43
|
+
# @param [String] target_var_name
|
44
|
+
def action(target_var_name)
|
45
|
+
@expression_handler.set_session_variable(
|
46
|
+
target_var_name,
|
47
|
+
sql_result
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
# argument types
|
52
|
+
def arg_definition
|
53
|
+
[:variable]
|
54
|
+
end
|
55
|
+
|
56
|
+
# returns value at first row/field in result set
|
57
|
+
def sql_result
|
58
|
+
JDBCHelpers::SingleValueFromQuery.new(
|
59
|
+
@jdbc_connection,
|
60
|
+
@statement,
|
61
|
+
@logger
|
62
|
+
).result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# executes a sql statement
|
67
|
+
class SneaqlExecute < Sneaql::Core::SneaqlCommand
|
68
|
+
Sneaql::Core::RegisterMappedClass.new(
|
69
|
+
:command,
|
70
|
+
'execute',
|
71
|
+
Sneaql::Core::Commands::SneaqlExecute
|
72
|
+
)
|
73
|
+
|
74
|
+
# execute sql statement
|
75
|
+
# last_statement_rows_affected is always set...
|
76
|
+
def action
|
77
|
+
@expression_handler.set_session_variable(
|
78
|
+
'last_statement_rows_affected',
|
79
|
+
rows_affected
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Fixnum] rows affected by SQL statement
|
84
|
+
def rows_affected
|
85
|
+
JDBCHelpers::Execute.new(
|
86
|
+
@jdbc_connection,
|
87
|
+
@statement,
|
88
|
+
@logger
|
89
|
+
).rows_affected
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# executes a sql statement if the condition evaluates to true
|
94
|
+
class SneaqlExecuteIf < Sneaql::Core::SneaqlCommand
|
95
|
+
Sneaql::Core::RegisterMappedClass.new(
|
96
|
+
:command,
|
97
|
+
'execute_if',
|
98
|
+
Sneaql::Core::Commands::SneaqlExecuteIf
|
99
|
+
)
|
100
|
+
|
101
|
+
# @param [String] left_value expression as left operand
|
102
|
+
# @param [String] operator comparison operator supported by expression handler
|
103
|
+
# @param [String] right_value expression as right operand
|
104
|
+
def action(left_value, operator, right_value)
|
105
|
+
if @expression_handler.compare_expressions(operator, left_value, right_value)
|
106
|
+
@expression_handler.set_session_variable(
|
107
|
+
'last_statement_rows_affected',
|
108
|
+
rows_affected
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# argument types
|
114
|
+
def arg_definition
|
115
|
+
[:expression, :operator, :expression]
|
116
|
+
end
|
117
|
+
|
118
|
+
# @return [Fixnum] rows affected by SQL statement
|
119
|
+
def rows_affected
|
120
|
+
JDBCHelpers::Execute.new(
|
121
|
+
@jdbc_connection,
|
122
|
+
@statement,
|
123
|
+
@logger
|
124
|
+
).rows_affected
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# compares the result of a sql statement against an argument
|
129
|
+
# raises error if the comparison does not evaluate to true
|
130
|
+
# the first field of the first record is used for the comparison
|
131
|
+
class SneaqlTest < Sneaql::Core::SneaqlCommand
|
132
|
+
Sneaql::Core::RegisterMappedClass.new(
|
133
|
+
:command,
|
134
|
+
'test',
|
135
|
+
Sneaql::Core::Commands::SneaqlTest
|
136
|
+
)
|
137
|
+
|
138
|
+
# @param [String] operator comparison operator supported by expression handler
|
139
|
+
# @param [String] value_to_test expression as right operand
|
140
|
+
def action(operator, value_to_test)
|
141
|
+
unless @expression_handler.compare_expressions(
|
142
|
+
operator,
|
143
|
+
sql_result,
|
144
|
+
value_to_test
|
145
|
+
)
|
146
|
+
raise Sneaql::Exceptions::SQLTestExitCondition
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# argument types
|
151
|
+
def arg_definition
|
152
|
+
[:operator, :expression]
|
153
|
+
end
|
154
|
+
|
155
|
+
# returns value at first row/field in result set
|
156
|
+
def sql_result
|
157
|
+
JDBCHelpers::SingleValueFromQuery.new(
|
158
|
+
@jdbc_connection,
|
159
|
+
@statement,
|
160
|
+
@logger
|
161
|
+
).result
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# raises an error to exit the transform if the condition evaluates to true
|
166
|
+
class SneaqlExitIf < Sneaql::Core::SneaqlCommand
|
167
|
+
Sneaql::Core::RegisterMappedClass.new(
|
168
|
+
:command,
|
169
|
+
'exit_if',
|
170
|
+
Sneaql::Core::Commands::SneaqlExitIf
|
171
|
+
)
|
172
|
+
|
173
|
+
# @param [String] operand1 expression as left operand
|
174
|
+
# @param [String] operator comparison operator supported by expression handler
|
175
|
+
# @param [String] operand2 expression as right operand
|
176
|
+
def action(operand1, operator, operand2)
|
177
|
+
if @expression_handler.compare_expressions(operator, operand1, operand2)
|
178
|
+
raise Sneaql::Exceptions::SQLTestExitCondition
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# argument types
|
183
|
+
def arg_definition
|
184
|
+
[:expression, :operator, :expression]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# raises an error to exit the transform step if the comdition evaluates to true
|
189
|
+
# note that this error needs to be handled accordingly in the calling
|
190
|
+
# procedure as all other errors will end the transform
|
191
|
+
class SneaqlExitStepIf < Sneaql::Core::SneaqlCommand
|
192
|
+
Sneaql::Core::RegisterMappedClass.new(
|
193
|
+
:command,
|
194
|
+
'exit_step_if',
|
195
|
+
Sneaql::Core::Commands::SneaqlExitStepIf
|
196
|
+
)
|
197
|
+
|
198
|
+
# @param [String] operand1 expression as left operand
|
199
|
+
# @param [String] operator comparison operator supported by expression handler
|
200
|
+
# @param [String] operand2 expression as right operand
|
201
|
+
def action(operand1, operator, operand2)
|
202
|
+
if @expression_handler.compare_expressions(operator, operand1, operand2)
|
203
|
+
raise Sneaql::Exceptions::SQLTestStepExitCondition
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# argument types
|
208
|
+
def arg_definition
|
209
|
+
[:expression, :operator, :expression]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# runs the query then stores the array of hashes into the recordset hash
|
214
|
+
class SneaqlRecordsetFromQuery < Sneaql::Core::SneaqlCommand
|
215
|
+
Sneaql::Core::RegisterMappedClass.new(
|
216
|
+
:command,
|
217
|
+
'recordset',
|
218
|
+
Sneaql::Core::Commands::SneaqlRecordsetFromQuery
|
219
|
+
)
|
220
|
+
|
221
|
+
# @param [String] recordset_name name of the recordset in which to store the results
|
222
|
+
def action(recordset_name)
|
223
|
+
r = query_results
|
224
|
+
@logger.debug "adding #{r.length} recs as #{recordset_name}"
|
225
|
+
@recordset_manager.store_recordset(recordset_name, r)
|
226
|
+
end
|
227
|
+
|
228
|
+
# argument types
|
229
|
+
def arg_definition
|
230
|
+
[:recordset]
|
231
|
+
end
|
232
|
+
|
233
|
+
# @return [Array] returns array of hashes from SQL results
|
234
|
+
def query_results
|
235
|
+
JDBCHelpers::QueryResultsToArray.new(
|
236
|
+
@jdbc_connection,
|
237
|
+
@statement,
|
238
|
+
@logger
|
239
|
+
).results
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# iterates a recordset and runs the sql statement for each record
|
244
|
+
class SneaqlIterateRecordset < Sneaql::Core::SneaqlCommand
|
245
|
+
Sneaql::Core::RegisterMappedClass.new(
|
246
|
+
:command,
|
247
|
+
'iterate',
|
248
|
+
Sneaql::Core::Commands::SneaqlIterateRecordset
|
249
|
+
)
|
250
|
+
|
251
|
+
# @param [*Array] args parameters for recordset expression in the format
|
252
|
+
def action(*args)
|
253
|
+
if args.size == 1
|
254
|
+
iterate_all_records(*args)
|
255
|
+
elsif ((args.size - 1) % 4) == 0
|
256
|
+
iterate_records_conditionally(*args)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# custom method for argument validation
|
261
|
+
# @param [Array] args
|
262
|
+
# @return [Boolean]
|
263
|
+
def validate_args(args)
|
264
|
+
if args.size == 1
|
265
|
+
return valid_recordset?(args[0])
|
266
|
+
elsif ((args.size - 1) % 4) == 0
|
267
|
+
valid = []
|
268
|
+
valid << valid_recordset?(args[0])
|
269
|
+
args[1..args.length - 1].each_slice(4) do |s|
|
270
|
+
if ['include', 'exclude'].include?(s[0])
|
271
|
+
valid << true
|
272
|
+
else
|
273
|
+
valid << false
|
274
|
+
end
|
275
|
+
# field names have the same rules as recordset names for now
|
276
|
+
valid << valid_recordset?(s[1])
|
277
|
+
valid << valid_operator?(s[2])
|
278
|
+
valid << valid_expression?(s[3])
|
279
|
+
end
|
280
|
+
!valid.include?(false)
|
281
|
+
else
|
282
|
+
return false
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# @param [String] recordset recordset to iterate
|
287
|
+
def iterate_all_records(recordset)
|
288
|
+
@logger.info "iterating recordset #{recordset}..."
|
289
|
+
@recordset_manager.recordset[recordset].each_with_index do |i, n|
|
290
|
+
@logger.debug("#{n + 1} of #{recordset}: #{i}")
|
291
|
+
tmp = @statement
|
292
|
+
i.keys.each { |k| tmp = tmp.gsub(":#{recordset}.#{k}", i[k].to_s) }
|
293
|
+
@expression_handler.set_session_variable(
|
294
|
+
'last_statement_rows_affected',
|
295
|
+
rows_affected_current_statement(tmp)
|
296
|
+
)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# @param [*Array] args all the arguments passed to the calling function
|
301
|
+
def iterate_records_conditionally(*args)
|
302
|
+
recordset = args.to_a[0]
|
303
|
+
@logger.info "iterating recordset #{recordset}..."
|
304
|
+
conditions = @recordset_manager.parse_recordset_expression(args.to_a)
|
305
|
+
@recordset_manager.recordset[recordset].each_with_index do |i, n|
|
306
|
+
@logger.debug("#{n + 1} of #{recordset}: #{i}")
|
307
|
+
next unless @recordset_manager.evaluate_expression_against_record(i, conditions)
|
308
|
+
tmp = @statement
|
309
|
+
i.keys.each { |k| tmp = tmp.gsub(":#{recordset}.#{k}", i[k].to_s) }
|
310
|
+
@expression_handler.set_session_variable(
|
311
|
+
'last_statement_rows_affected',
|
312
|
+
rows_affected_current_statement(tmp)
|
313
|
+
)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# @return [Fixnum] rows affected by the SQL statement
|
318
|
+
def rows_affected_current_statement(stmt)
|
319
|
+
JDBCHelpers::Execute.new(
|
320
|
+
@jdbc_connection,
|
321
|
+
stmt,
|
322
|
+
@logger
|
323
|
+
).rows_affected
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
# stores all the file paths matching the dir glob into a recordset
|
328
|
+
class SneaqlRecordsetFromDirGlob < Sneaql::Core::SneaqlCommand
|
329
|
+
Sneaql::Core::RegisterMappedClass.new(
|
330
|
+
:command,
|
331
|
+
'rs_from_local_dir',
|
332
|
+
Sneaql::Core::Commands::SneaqlRecordsetFromDirGlob
|
333
|
+
)
|
334
|
+
|
335
|
+
# @param [String] recordset_name
|
336
|
+
# @param [String] dirglob directory glob with optional wildcards
|
337
|
+
def action(recordset_name, dirglob)
|
338
|
+
r = Dir.glob(dirglob)
|
339
|
+
r.map! { |d| { 'path_name' => d.to_s } }
|
340
|
+
@logger.debug "adding #{r.length} recs as #{recordset_name}"
|
341
|
+
@recordset_manager.store_recordset(recordset_name, r)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Sneaql
|
2
|
+
module Core
|
3
|
+
# returns the database type based upon the jdbc url
|
4
|
+
# @param [String]
|
5
|
+
def self.database_type(jdbc_url)
|
6
|
+
Sneaql::Core.class_map[:database].each do |d|
|
7
|
+
return d[:text] if jdbc_url.match(d[:text])
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Manages preferences for a specific RDBMS implementation.
|
12
|
+
class DatabaseManager
|
13
|
+
attr_reader(
|
14
|
+
:has_boolean,
|
15
|
+
:autocommit_off_statement,
|
16
|
+
:supports_transactions,
|
17
|
+
:supports_table_locking,
|
18
|
+
:begin_statement,
|
19
|
+
:commit_statement,
|
20
|
+
:rollback_statement
|
21
|
+
)
|
22
|
+
|
23
|
+
# @param [Hash] options values to override defaults
|
24
|
+
def initialize(options = {})
|
25
|
+
@has_boolean = options.fetch(:has_boolean, default_has_boolean)
|
26
|
+
@autocommit_off_statement = options.fetch(:autocommit_off_statement, default_autocommit_off_statement)
|
27
|
+
@supports_transactions = options.fetch(:supports_transactions, default_supports_transactions)
|
28
|
+
@supports_table_locking = options.fetch(:supports_table_locking, default_supports_table_locking)
|
29
|
+
@begin_statement = options.fetch(:begin_statement, default_begin_statement)
|
30
|
+
@commit_statement = options.fetch(:commit_statement, default_commit_statement)
|
31
|
+
@rollback_statement = options.fetch(:rollback_statement, default_rollback_statement)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean]
|
35
|
+
def default_has_boolean
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String]
|
40
|
+
def default_autocommit_off_statement
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Boolean]
|
45
|
+
def default_supports_transactions
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Boolean]
|
50
|
+
def default_supports_table_locking
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [String] begin statement
|
55
|
+
def default_begin_statement
|
56
|
+
"begin;"
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [String] commit statement
|
60
|
+
def default_commit_statement
|
61
|
+
"commit;"
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [String] rollback statement
|
65
|
+
def default_rollback_statement
|
66
|
+
"rollback;"
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param [String] table_name
|
70
|
+
# @return [String] rollback statement
|
71
|
+
def lock_table_statement(table_name)
|
72
|
+
"lock table #{table_name};"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Require all tested RDBMS
|
79
|
+
Dir.glob("#{File.dirname(__FILE__)}/database_prefs/*.rb").each { |f| require File.expand_path(f) }
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sneaql
|
2
|
+
module Core
|
3
|
+
# Settings for interaction with Amazon Redshift
|
4
|
+
class RedshiftDatabaseManager < Sneaql::Core::DatabaseManager
|
5
|
+
Sneaql::Core::RegisterMappedClass.new(
|
6
|
+
:database,
|
7
|
+
'redshift',
|
8
|
+
Sneaql::Core::RedshiftDatabaseManager
|
9
|
+
)
|
10
|
+
|
11
|
+
def initialize()
|
12
|
+
super(
|
13
|
+
{
|
14
|
+
has_boolean: true,
|
15
|
+
autocommit_off_statement: 'set autocommit=off;'
|
16
|
+
}
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Sneaql
|
2
|
+
module Core
|
3
|
+
# Settings for interacting with SQLite
|
4
|
+
class SqliteDatabaseManager < Sneaql::Core::DatabaseManager
|
5
|
+
Sneaql::Core::RegisterMappedClass.new(
|
6
|
+
:database,
|
7
|
+
'sqlite',
|
8
|
+
Sneaql::Core::SqliteDatabaseManager
|
9
|
+
)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sneaql
|
2
|
+
module Core
|
3
|
+
# Settings for interacting with HP Vertica
|
4
|
+
class VerticaDatabaseManager < Sneaql::Core::DatabaseManager
|
5
|
+
Sneaql::Core::RegisterMappedClass.new(
|
6
|
+
:database,
|
7
|
+
'vertica',
|
8
|
+
Sneaql::Core::VerticaDatabaseManager
|
9
|
+
)
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super(
|
13
|
+
{
|
14
|
+
has_boolean: true,
|
15
|
+
autocommit_off_statement: 'set session autocommit to off;'
|
16
|
+
}
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|