sneaql 0.0.13-java → 0.0.15-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  require 'logger'
2
+ require 'time'
2
3
 
3
4
  module Sneaql
4
5
  module Core
@@ -21,7 +22,7 @@ module Sneaql
21
22
  # @param [String, Fixnum, Float] var_value value to store, expressions here will not be evaluated
22
23
  def set_session_variable(var_name, var_value)
23
24
  @logger.info("setting session var #{var_name} to #{var_value}")
24
- raise "can't set environment variable #{var_name}" unless valid_session_variable_name?(var_name)
25
+ raise "can't set variable #{var_name}" unless valid_session_variable_name?(var_name)
25
26
  @session_variables[var_name] = var_value
26
27
  end
27
28
 
@@ -29,8 +30,8 @@ module Sneaql
29
30
  # @param [String] var_name
30
31
  # @return [Boolean]
31
32
  def valid_session_variable_name?(var_name)
32
- r = (var_name.to_s.match(/^\w+$/) && !var_name.to_s.match(/env\_\w*/) && !var_name.to_s.match(/^\d+/)) ? true : false
33
- @logger.debug "validating #{var_name} as valid variable identifier indicates #{r}"
33
+ r = var_name.to_s.match(/^\w+$/) && !var_name.to_s.match(/env\_\w*/) && !var_name.to_s.match(/^\d+/) ? true : false
34
+ @logger.debug("validating #{var_name} as valid variable identifier indicates #{r}")
34
35
  r
35
36
  end
36
37
 
@@ -57,21 +58,25 @@ module Sneaql
57
58
  def evaluate_expression(expression)
58
59
  return expression unless expression.class == String
59
60
 
61
+ # first handle empty string
62
+ if expression == "''"
63
+ return ''
64
+
60
65
  # reference to an environment variable
61
66
  # :env_var_name or :ENV_var_name
62
67
  # env variable references are case insensitive in this case
63
- if expression =~ /\:env\_\w+/i
68
+ elsif expression =~ /^\:env\_\w+/i
64
69
  return @environment_variables[expression.gsub(/\:env\_/i, '').strip]
65
70
 
66
71
  # reference to a variable
67
72
  # ANSI dynamic SQL :var_name
68
73
  # variable names are case sensitive
69
- elsif expression =~ /\:\w+/
74
+ elsif expression =~ /^\:\w+/
70
75
  return @session_variables[expression.gsub(/\:/, '').strip]
71
76
 
72
77
  # deprecated
73
- elsif expression =~ /\{.*\}/
74
- @logger.warn '{var_name} deprecated. use dynamic SQL syntax :var_name'
78
+ elsif expression =~ /^\{.*\}$/
79
+ @logger.warn('{var_name} deprecated. use dynamic SQL syntax :var_name')
75
80
  return @session_variables[expression.gsub(/\{|\}/, '').strip]
76
81
 
77
82
  # boolean
@@ -81,9 +86,9 @@ module Sneaql
81
86
  return false if expression.downcase == 'false'
82
87
 
83
88
  # string literal enclosed in single quotes
84
- # only works for a single word... no whitespace allowed at this time
85
- elsif expression =~ /\'.*\'/
86
- return expression.delete("'").strip
89
+
90
+ elsif expression.match(/^\s*\'.*\'\s*$/)
91
+ return expression.strip[1..expression.strip.length - 2]
87
92
 
88
93
  # else assume it is a numeric literal
89
94
  # need some better thinking here
@@ -92,7 +97,7 @@ module Sneaql
92
97
  end
93
98
  rescue => e
94
99
  @logger.error("error evaluating expression: #{e.message}")
95
- e.backtrace.each { |b| logger.error(b.to_s) }
100
+ e.backtrace.each { |b| @logger.error(b.to_s) }
96
101
  raise Sneaql::Exceptions::ExpressionEvaluationError
97
102
  end
98
103
 
@@ -117,11 +122,13 @@ module Sneaql
117
122
  # evaluates all environment variables in a given SQL statement.
118
123
  # replaces...
119
124
  # environment variables in the form :env_HOSTNAME
125
+ # variables are sorted and reversed before performing substitutions
126
+ # this prevents :var from partially overwriting :var_foo
120
127
  # @param [String] statement SQL statement to have all environment variables evaluated
121
128
  # @return [String] SQL statement with all variable references resolved
122
129
  def evaluate_session_variables(statement)
123
130
  # replaces :var_name in provided statement
124
- @session_variables.keys.each do |k|
131
+ @session_variables.keys.sort.reverse.each do |k|
125
132
  statement.gsub!(/\:#{k}/, @session_variables[k].to_s)
126
133
  end
127
134
  end
@@ -129,11 +136,13 @@ module Sneaql
129
136
  # evaluates all session variables in a given SQL statement.
130
137
  # replaces...
131
138
  # session variables in the form :variable_name
139
+ # variables are sorted and reversed before performing substitutions
140
+ # this prevents :var from partially overwriting :var_foo
132
141
  # @param [String] statement SQL statement to have all session variables evaluated
133
142
  # @return [String] SQL statement with all variable references resolved
134
143
  def evaluate_environment_variables(statement)
135
144
  # replace env vars in the form :env_HOSTNAME
136
- @environment_variables.keys.each do |e|
145
+ @environment_variables.keys.sort.reverse.each do |e|
137
146
  statement.gsub!(/\:env\_#{e}/i, @environment_variables[e])
138
147
  end
139
148
  end
@@ -155,7 +164,7 @@ module Sneaql
155
164
  # checks to see this is single quoted string, :variable_name, {var_name) or number (1, 1.031, etc.)
156
165
  # @param [String] expr value to check
157
166
  def valid_expression_reference?(expr)
158
- return expr.to_s.match(/(^\'.+\'$|^\:\w+$|^\{\w+\}$|^\d+$|^\d+\.\d*$|true|false)/i) ? true : false
167
+ expr.to_s.match(/(^\s*\'.+\'\s*$|^\:\w+$|^\{\w+\}$|^\d+$|^\d+\.\d*$|true|false)/i) ? true : false
159
168
  end
160
169
 
161
170
  # Operators valid for expression comparison
@@ -183,6 +192,7 @@ module Sneaql
183
192
  exp1,
184
193
  exp2
185
194
  )
195
+
186
196
  compare_values(operator, coerced[0], coerced[1])
187
197
  end
188
198
 
@@ -195,16 +205,31 @@ module Sneaql
195
205
  @logger.debug("coercing types #{[exp1.class, exp2.class]}")
196
206
  if exp1.class == exp2.class
197
207
  nil # nothing to do... continue with comparison
198
- elsif ([exp1.class, exp2.class].include?(FalseClass)) or ([exp1.class, exp2.class].include?(TrueClass))
208
+ elsif array_has_boolean_value?([exp1, exp2])
209
+ # if one of the values is an actual Boolean object...
199
210
  unless [coerce_boolean(exp1), coerce_boolean(exp2)].include?(nil)
200
211
  exp1 = coerce_boolean(exp1)
201
212
  exp2 = coerce_boolean(exp2)
202
213
  end
203
- elsif [exp1.class, exp2.class].include? Float
214
+ elsif exp1.class == Time
215
+ tmp = coerce_datetime(exp2)
216
+ if tmp
217
+ exp2 = tmp
218
+ else
219
+ exp1, exp2 = exp1.to_s, exp2.to_s
220
+ end
221
+ elsif exp2.class == Time
222
+ tmp = coerce_datetime(exp1)
223
+ if tmp
224
+ exp1 = tmp
225
+ else
226
+ exp1, exp2 = exp1.to_s, exp2.to_s
227
+ end
228
+ elsif [exp1.class, exp2.class].include?(Float)
204
229
  # if either is a float then make sure they are both floats
205
230
  exp1 = exp1.to_f
206
231
  exp2 = exp2.to_f
207
- elsif [exp1.class, exp2.class].include? Fixnum
232
+ elsif [exp1.class, exp2.class].include?(Fixnum)
208
233
  # otherwise... if one is an integer make them both integers
209
234
  exp1 = exp1.to_i
210
235
  exp2 = exp2.to_i
@@ -213,26 +238,52 @@ module Sneaql
213
238
  [exp1, exp2]
214
239
  end
215
240
 
241
+ def coerce_datetime(obj)
242
+ retobj = nil
243
+ case
244
+ when obj.class == Fixnum then retobj = Time.at(obj)
245
+ when obj.class == String then retobj = Time.parse(obj)
246
+ when obj.class == Time then retobj = obj
247
+ end
248
+ retobj
249
+ end
250
+
251
+ # checks to see if any element of the array has a boolean value
252
+ # @param [Array<Object>] arr array of objects
253
+ # @return [Boolean]
254
+ def array_has_boolean_value?(arr)
255
+ tmp = arr.map { |e| e.class }
256
+ # [exp1.class, exp2.class].include?(FalseClass) || [exp1.class, exp2.class].include?(TrueClass)
257
+ tmp.include?(FalseClass) || tmp.include?(TrueClass)
258
+ end
259
+
216
260
  # evaluates string or fixnum values to coerce into boolean
217
261
  # @param [Object] value
218
262
  # @return [Object]
219
263
  def coerce_boolean(value)
220
264
  retval = nil
221
- if [TrueClass, FalseClass].include?(value.class)
265
+ if array_has_boolean_value?([value])
222
266
  retval = value
223
267
  elsif value.class == Fixnum
224
268
  retval = true if value == 1
225
- retval = false if value == 0
269
+ retval = false if value.zero?
226
270
  elsif value.class == String
227
- tmp = value.downcase.strip
228
- case tmp
229
- when 'f', 'false', '0'
230
- retval = false
231
- when 't', 'true', '1'
232
- retval = true
233
- end
271
+ retval = text_to_boolean(value)
272
+ end
273
+ retval
274
+ end
275
+
276
+ # converts text representation of boolean to boolean
277
+ # @param [String] value
278
+ # @return [Boolean]
279
+ def text_to_boolean(value)
280
+ case value.downcase.strip
281
+ when 'f', 'false', '0'
282
+ retval = false
283
+ when 't', 'true', '1'
284
+ retval = true
234
285
  end
235
- return retval
286
+ retval
236
287
  end
237
288
 
238
289
  # performs the actual comparison between two values
@@ -260,15 +311,15 @@ module Sneaql
260
311
  # @param [String] like_right_operand this will be the like expression
261
312
  # @return [Boolean]
262
313
  def like_operator(left_operand, like_right_operand)
263
- #converts to string before comparison
264
- return left_operand.to_s.match(wildcard_to_regex(like_right_operand.to_s)) ? true : false
314
+ # converts to string before comparison
315
+ left_operand.to_s.match(wildcard_to_regex(like_right_operand.to_s)) ? true : false
265
316
  end
266
317
 
267
318
  # converts a SQL LIKE wildcard expression to a Regexp
268
319
  # @param [String] wildcard like expression
269
320
  # @return [Regexp] returns regexp object for use in match comparison
270
321
  def wildcard_to_regex(wildcard)
271
- Regexp.new("^#{wildcard}$".gsub('%','.*').gsub('_','.'))
322
+ Regexp.new("^#{wildcard}$".gsub('%', '.*').gsub('_', '.'))
272
323
  end
273
324
 
274
325
  # create a hash built from supplied environment variables.
@@ -278,14 +329,14 @@ module Sneaql
278
329
  def filtered_environment_variables
279
330
  env_vars = {}
280
331
  if ENV['SNEAQL_AVAILABLE_ENV_VARS']
281
- @logger.debug("filtering environment variables")
332
+ @logger.debug('filtering environment variables')
282
333
  available = ENV['SNEAQL_AVAILABLE_ENV_VARS'].split(',')
283
334
  ENV.keys.each { |k| env_vars[k] = ENV[k] if available.include?(k) }
284
335
  else
285
- @logger.debug("setting environment variables")
336
+ @logger.debug('setting environment variables')
286
337
  ENV.keys.each { |k| env_vars[k] = ENV[k] }
287
338
  end
288
- return env_vars
339
+ env_vars
289
340
  end
290
341
 
291
342
  # basic regex filtering to help combat sql injection
@@ -294,7 +345,7 @@ module Sneaql
294
345
  # @return [Boolean] returns true if value is safe
295
346
  def sql_injection_filter(value)
296
347
  return false if value.to_s.match(/(\'|\;|(drop|alter).*(table|user|view|column|database|schema|function|sequence|procedure))/i)
297
- return true
348
+ true
298
349
  end
299
350
 
300
351
  # insures that all environment variables pass SQL injection test
@@ -44,7 +44,7 @@ module Sneaql
44
44
  lock_value = false
45
45
 
46
46
  if @database_manager.supports_transactions == true
47
- l = JDBCHelpers::Execute.new(
47
+ JDBCHelpers::Execute.new(
48
48
  jdbc_connection,
49
49
  @database_manager.begin_statement,
50
50
  @logger
@@ -52,7 +52,7 @@ module Sneaql
52
52
  end
53
53
 
54
54
  if @database_manager.supports_table_locking == true
55
- l = JDBCHelpers::Execute.new(
55
+ JDBCHelpers::Execute.new(
56
56
  jdbc_connection,
57
57
  @database_manager.lock_table_statement(@transform_lock_table),
58
58
  @logger
@@ -94,7 +94,7 @@ module Sneaql
94
94
  )
95
95
 
96
96
  if @database_manager.supports_transactions == true
97
- l = JDBCHelpers::Execute.new(
97
+ JDBCHelpers::Execute.new(
98
98
  jdbc_connection,
99
99
  @database_manager.commit_statement,
100
100
  @logger
@@ -104,7 +104,7 @@ module Sneaql
104
104
  lock_value = true
105
105
  else
106
106
  if @database_manager.supports_transactions == true
107
- l = JDBCHelpers::Execute.new(
107
+ JDBCHelpers::Execute.new(
108
108
  jdbc_connection,
109
109
  @database_manager.rollback_statement,
110
110
  @logger
@@ -132,7 +132,7 @@ module Sneaql
132
132
  jdbc_connection = create_jdbc_connection
133
133
 
134
134
  if @database_manager.supports_transactions == true
135
- l = JDBCHelpers::Execute.new(
135
+ JDBCHelpers::Execute.new(
136
136
  jdbc_connection,
137
137
  @database_manager.begin_statement,
138
138
  @logger
@@ -140,7 +140,7 @@ module Sneaql
140
140
  end
141
141
 
142
142
  if @database_manager.supports_table_locking == true
143
- l = JDBCHelpers::Execute.new(
143
+ JDBCHelpers::Execute.new(
144
144
  jdbc_connection,
145
145
  @database_manager.lock_table_statement(@transform_lock_table),
146
146
  @logger
@@ -163,7 +163,7 @@ module Sneaql
163
163
  )
164
164
  ensure
165
165
  jdbc_connection.close
166
-
166
+
167
167
  return true
168
168
  end
169
169
 
@@ -1,3 +1,5 @@
1
+ require_relative 'tokenizer.rb'
2
+
1
3
  module Sneaql
2
4
  module Core
3
5
  # Parses a step file into discrete statements.
@@ -10,7 +12,7 @@ module Sneaql
10
12
  # @param [String] file_path pathname to step file
11
13
  # @param [Sneaql::ExpressionHandler] expression_handler
12
14
  # @param [Sneaql::RecordsetManager] recordset_manager
13
- # @param [Logger] logger optional logger, if omitted default logger will be used
15
+ # @param [Logger] logger optional, if omitted default logger will be used
14
16
  def initialize(file_path, expression_handler, recordset_manager, logger = nil)
15
17
  @logger = logger ? logger : Logger.new(STDOUT)
16
18
  @expression_handler = expression_handler
@@ -39,12 +41,16 @@ module Sneaql
39
41
  raise Sneaql::Exceptions::StatementParsingError
40
42
  end
41
43
 
42
- # Extracts tag and splits into an array
44
+ # Extracts array of tokens from tag
43
45
  # @param [String] statement_text_with_command
44
46
  # @return [Array]
45
47
  def tag_splitter(statement_text_with_command)
48
+ # updated to use tokenizer
46
49
  # splits out all the tag elements into an array
47
- statement_text_with_command.split('-*/')[0].gsub('/*-', '').strip.split
50
+ # statement_text_with_command.split('-*/')[0].gsub('/*-', '').strip.split
51
+ command = statement_text_with_command.split('-*/')[0].gsub('/*-', '').strip
52
+ t = Sneaql::Core::Tokenizer.new
53
+ t.tokenize(command)
48
54
  end
49
55
 
50
56
  # Returns command tag from statement at specified index. Allows for
@@ -78,6 +84,7 @@ module Sneaql
78
84
  c = Sneaql::Core.find_class(:command, this_cmd[:command]).new(
79
85
  nil,
80
86
  @expression_handler,
87
+ nil,
81
88
  @recordset_manager,
82
89
  nil,
83
90
  @logger
@@ -6,7 +6,7 @@ require_relative 'base.rb'
6
6
  module Sneaql
7
7
  # Classes to manage repositories full of SneaQL code.
8
8
  module RepoManagers
9
-
9
+
10
10
  # tells you the repo type based upon the url
11
11
  # either git or http
12
12
  # @param [String] repo_url
@@ -14,8 +14,7 @@ module Sneaql
14
14
  return 'git' if repo_url.match(/\.*git.*/i)
15
15
  return 'http' if repo_url.match(/\.*http.*/i)
16
16
  end
17
-
18
-
17
+
19
18
  # pulls a branch from a remote git repo
20
19
  class GitRepoManager < Sneaql::Core::RepoDownloadManager
21
20
  Sneaql::Core::RegisterMappedClass.new(
@@ -39,19 +39,19 @@ module Sneaql
39
39
  )
40
40
  return true
41
41
  end
42
-
42
+
43
43
  # Coerces a boolean to the appropriate value for the database type.
44
44
  # May return a 0 or 1 in RDBMS where boolean is not supported.
45
45
  # @param [Boolean] boolean_value
46
46
  # @return [Boolean, Fixnum]
47
47
  def coerce_boolean(boolean_value)
48
- if @database_manager.has_boolean
49
- boolean_value
50
- else
51
- boolean_value == true ? 1 : 0
48
+ if @database_manager.has_boolean
49
+ boolean_value
50
+ else
51
+ boolean_value == true ? 1 : 0
52
52
  end
53
53
  end
54
-
54
+
55
55
  # Create table statement for primary transform table.
56
56
  # @param [String] transform_table_name fully qualified name for this table
57
57
  # @return [String]
@@ -68,7 +68,7 @@ module Sneaql
68
68
  ,updated_ts timestamp
69
69
  );}
70
70
  end
71
-
71
+
72
72
  # Creates a record in the transforms table.
73
73
  # @param [String] transform_table_name
74
74
  # @param [Hash] params Hash of parameters with symbols matching column names
@@ -106,7 +106,7 @@ module Sneaql
106
106
  ,current_timestamp
107
107
  );}
108
108
  end
109
-
109
+
110
110
  # Drops and recreates the transform steps table.
111
111
  # @param [String] transform_steps_table_name fully qualified name for this table
112
112
  # @return [Boolean]
@@ -10,14 +10,21 @@ module Sneaql
10
10
  'local_file',
11
11
  Sneaql::StepManagers::JSONFileStepManager
12
12
  )
13
-
13
+
14
14
  # Manages steps from a local JSON file.
15
15
  def manage_steps
16
- @steps = JSON.parse(File.read(@params[:step_metadata_file_path])).sort_by! { |h| h['step_number'] }
17
- @steps.map! { |j| { step_number: j['step_number'], step_file: j['step_file'] } }
16
+ @steps = JSON.parse(
17
+ File.read(@params[:step_metadata_file_path])
18
+ ).sort_by! { |h| h['step_number'] }
19
+ @steps.map! do |j|
20
+ {
21
+ step_number: j['step_number'],
22
+ step_file: j['step_file']
23
+ }
24
+ end
18
25
  end
19
26
  end
20
-
27
+
21
28
  # source step metadata from a standardized table in the target database
22
29
  class TransformStepTableManager < Sneaql::Core::StepMetadataManager
23
30
  Sneaql::Core::RegisterMappedClass.new(
@@ -25,7 +32,7 @@ module Sneaql
25
32
  'transform_steps_table',
26
33
  Sneaql::StepManagers::TransformStepTableManager
27
34
  )
28
-
35
+
29
36
  # Manages steps based in a standardized table.
30
37
  def manage_steps
31
38
  jdbc_connection = JDBCHelpers::ConnectionFactory.new(
@@ -33,28 +40,35 @@ module Sneaql
33
40
  @params[:db_user],
34
41
  @params[:db_pass]
35
42
  ).connection
36
-
43
+
37
44
  @steps = JDBCHelpers::QueryResultsToArray.new(
38
45
  jdbc_connection,
39
- %(select
40
- transform_step
41
- ,sql_file_path_in_repo
42
- from
43
- #{@params[:transform_steps_table]}
44
- where
45
- transform_name='#{@params[:transform_name]}'
46
- and
47
- is_active=#{ if @params[:database_manager].has_boolean then 'true' else 1 end }
48
- order by
49
- transform_step asc;)
46
+ steps_sql
50
47
  ).results
51
-
52
- @steps.map! do |s|
53
- { step_number: s['transform_step'], step_file: s['sql_file_path_in_repo'] }
48
+
49
+ @steps.map! do |s|
50
+ {
51
+ step_number: s['transform_step'],
52
+ step_file: s['sql_file_path_in_repo']
53
+ }
54
54
  end
55
-
55
+
56
56
  jdbc_connection.close
57
57
  end
58
+
59
+ def steps_sql
60
+ %(select
61
+ transform_step
62
+ ,sql_file_path_in_repo
63
+ from
64
+ #{@params[:transform_steps_table]}
65
+ where
66
+ transform_name='#{@params[:transform_name]}'
67
+ and
68
+ is_active=#{@params[:database_manager].has_boolean ? 'true' : 1}
69
+ order by
70
+ transform_step asc;)
71
+ end
58
72
  end
59
73
  end
60
74
  end