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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1691a3f7c5849f131d0c93b224dec808c78877d1
4
- data.tar.gz: 6ecfdbd3c59590059ae603b85c6694ebab7174d0
3
+ metadata.gz: 91f871ef54dcf89b3664fc1752bfc13cfb6ce787
4
+ data.tar.gz: fd38d7bf1c632db20ae61ae88ec57872a370edf9
5
5
  SHA512:
6
- metadata.gz: d38ec6c6d5ffcafd8090163e47e0ae3d77691ed8411a469fe1e653b24e7d88dbda0fb6b16bcf7ca373c27b0988801355960abecbccc2c7f528165acb50dac26a
7
- data.tar.gz: 8c584cfeb4e4dfd5655aaf3bc6e19bb1da042281ef9c09dab2a0b0f593d8b28abddccbb95d91068795c718f67f950a717e575aba910e1dc02a91b17b03e0ea7b
6
+ metadata.gz: a5d6fa48016600fde9277250a4b7bdde4f3d1e3cee7c6305af06b9b084ed00c8d2ea21739315cb68472a4b348b1b99858468d71a1bc9a08868e454c34fdb5df7
7
+ data.tar.gz: 9bd0b13170e52f50b961b65228a139f1c879ccd4780d3610665aecd6698741b15232fbd9023453f25c6ea121467a4126f51b878e3440a15003a5045d22cc2c5c
data/bin/sneaql CHANGED
@@ -36,7 +36,6 @@ def configure_logging_format
36
36
  end
37
37
  end
38
38
 
39
-
40
39
  def local_gems
41
40
  Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.group_by{ |g| g.name }
42
41
  end
data/lib/sneaql.rb CHANGED
@@ -13,6 +13,7 @@ require_relative 'sneaql_lib/recordset.rb'
13
13
  require_relative 'sneaql_lib/database_manager.rb'
14
14
  require_relative 'sneaql_lib/standard_db_objects.rb'
15
15
  require_relative 'sneaql_lib/docker.rb'
16
+ require_relative 'sneaql_lib/tokenizer.rb'
16
17
 
17
18
  # module for sneaql
18
19
  module Sneaql
@@ -77,13 +78,15 @@ module Sneaql
77
78
  @db_pass = @params[:db_pass]
78
79
  @database = @params[:database]
79
80
 
81
+ @expression_handler = create_expression_handler
82
+ @recordset_manager = create_recordset_manager
83
+ @exception_manager = create_exception_manager
84
+
80
85
  run if @params[:run] == true
81
86
  end
82
87
 
83
88
  # validate the transform.
84
89
  def validate
85
- @expression_handler = create_expression_handler
86
- @recordset_manager = create_recordset_manager
87
90
  @repo_manager = create_repo_manager
88
91
  @steps = create_metadata_manager
89
92
  @parsed_steps = create_parsed_steps(@steps)
@@ -116,8 +119,6 @@ module Sneaql
116
119
 
117
120
  # Runs the actual transform.
118
121
  def run
119
- @expression_handler = create_expression_handler
120
- @recordset_manager = create_recordset_manager
121
122
  @repo_manager = create_repo_manager
122
123
  @lock_manager = create_lock_manager if @params[:locked_transform] == true
123
124
  @steps = create_metadata_manager
@@ -159,6 +160,11 @@ module Sneaql
159
160
  Sneaql::Core::ExpressionHandler.new(@logger)
160
161
  end
161
162
 
163
+ # Creates ExceptionHandler object
164
+ def create_exception_manager
165
+ Sneaql::Exceptions::ExceptionManager.new(@logger)
166
+ end
167
+
162
168
  # Creates a RepoDownloadManager object
163
169
  # The actual object returns depends upon params[:repo_type] provided at initialize.
164
170
  # @return [Sneaql::Core::RepoDownloadManager]
@@ -236,9 +242,9 @@ module Sneaql
236
242
  # not rollback automatically unless that is the default RDBMS
237
243
  # behavior for a connection that closes before a commit.
238
244
  def iterate_steps_and_statements
239
- @parsed_steps.each do |this_step|
240
- #special handling is required for the exit_step_if command
241
- #because there is a nested loop the exit_step var is needed
245
+ @parsed_steps.each_with_index do |this_step|
246
+ # special handling is required for the exit_step_if command
247
+ # because there is a nested loop the exit_step var is needed
242
248
  exit_step = false
243
249
  break if exit_step == true
244
250
  # set this so that other processes can poll the state
@@ -247,7 +253,9 @@ module Sneaql
247
253
  this_step[:parser].statements.each_with_index do |this_stmt, stmt_index|
248
254
  # set this so that other processes can poll the state
249
255
  @current_statement = stmt_index + 1
250
-
256
+
257
+ @exception_manager.output_pending_error
258
+
251
259
  # log some useful info
252
260
  @logger.info("step: #{@current_step} statement: #{@current_statement}")
253
261
  @expression_handler.output_all_session_variables
@@ -267,12 +275,24 @@ module Sneaql
267
275
  c = Sneaql::Core.find_class(:command, this_cmd[:command]).new(
268
276
  @jdbc_connection,
269
277
  @expression_handler,
278
+ @exception_manager,
270
279
  @recordset_manager,
271
280
  @expression_handler.evaluate_all_expressions(this_stmt),
272
281
  @logger
273
282
  )
274
283
 
275
284
  c.action(*this_cmd[:arguments])
285
+
286
+ # check if there was an error from the action
287
+ if @exception_manager.pending_error != nil
288
+ # if there was an error... check to see if this is the last stmt in step
289
+ if stmt_index == (this_step[:parser].statements.length - 1)
290
+ # last step... so we know there is no error handler in this step
291
+ # therefore we should propagate the error
292
+ raise @exception_manager.pending_error
293
+ end
294
+ end
295
+
276
296
  rescue Sneaql::Exceptions::SQLTestStepExitCondition => e
277
297
  exit_step = true
278
298
  @logger.info e.message
@@ -2,7 +2,7 @@ require 'zip/zip'
2
2
  require 'fileutils'
3
3
  require 'logger'
4
4
 
5
- #top level namespace for sneaql objects
5
+ # top level namespace for sneaql objects
6
6
  module Sneaql
7
7
  # contains the base classes for the extendable parts of sneaql:
8
8
  # commands (the actual commands specified in sneaql tags)
@@ -74,11 +74,12 @@ module Sneaql
74
74
  # @param [Sneaql::Core::RecordsetManager] recordset_manager
75
75
  # @param [String] statement SQL statement provided in body, with all variables resolved
76
76
  # @param [Logger] logger object otherwise will default to new Logger
77
- def initialize(jdbc_connection, expression_handler, recordset_manager, statement, logger = nil)
77
+ def initialize(jdbc_connection, expression_handler, exception_manager, recordset_manager, statement, logger = nil)
78
78
  @logger = logger ? logger : Logger.new(STDOUT)
79
79
 
80
80
  @jdbc_connection = jdbc_connection
81
81
  @expression_handler = expression_handler
82
+ @exception_manager = exception_manager
82
83
  @statement = statement
83
84
  @recordset_manager = recordset_manager
84
85
  end
@@ -110,6 +111,8 @@ module Sneaql
110
111
  valid << valid_operator?(a)
111
112
  when arg_definition[i] == :recordset then
112
113
  valid << valid_recordset?(a)
114
+ when arg_definition[i] == :symbol then
115
+ valid << valid_symbol?(a)
113
116
  else valid << false end
114
117
  end
115
118
  @logger.debug("arg validation results: #{valid}")
@@ -143,6 +146,16 @@ module Sneaql
143
146
  def valid_recordset?(a)
144
147
  @recordset_manager.valid_recordset_name?(a.to_s.strip)
145
148
  end
149
+
150
+ # note that this is not a ruby symbol, but a sneaql symbol
151
+ # should be a contigiuous string consisting of only letters, digits
152
+ # and underscores
153
+ # @param [String] a value to test
154
+ # @return [Boolean]
155
+ def valid_symbol?(a)
156
+ return true if a.match(/^\w+$/)
157
+ false
158
+ end
146
159
 
147
160
  private
148
161
 
@@ -5,7 +5,7 @@ require_relative 'exceptions.rb'
5
5
  module Sneaql
6
6
  module Core
7
7
  # Core Sneaql language command tags.
8
- # You can create your own tags by extending
8
+ # You can create your own tags by extending
9
9
  # Sneaql::Core::SneaqlCommand and overriding the
10
10
  # action method. You should also override arg_definition
11
11
  # and potentially validate_args if have a complex argument
@@ -23,6 +23,8 @@ module Sneaql
23
23
  # @param [String] value expression (must be a string)
24
24
  def action(var_name, value)
25
25
  @expression_handler.set_session_variable(var_name, value)
26
+ rescue => e
27
+ @exception_manager.pending_error = e
26
28
  end
27
29
 
28
30
  # argument types
@@ -46,6 +48,8 @@ module Sneaql
46
48
  target_var_name,
47
49
  sql_result
48
50
  )
51
+ rescue => e
52
+ @exception_manager.pending_error = e
49
53
  end
50
54
 
51
55
  # argument types
@@ -78,6 +82,8 @@ module Sneaql
78
82
  'last_statement_rows_affected',
79
83
  rows_affected
80
84
  )
85
+ rescue => e
86
+ @exception_manager.pending_error = e
81
87
  end
82
88
 
83
89
  # @return [Fixnum] rows affected by SQL statement
@@ -108,6 +114,8 @@ module Sneaql
108
114
  rows_affected
109
115
  )
110
116
  end
117
+ rescue => e
118
+ @exception_manager.pending_error = e
111
119
  end
112
120
 
113
121
  # argument types
@@ -145,6 +153,8 @@ module Sneaql
145
153
  )
146
154
  raise Sneaql::Exceptions::SQLTestExitCondition
147
155
  end
156
+ rescue => e
157
+ @exception_manager.pending_error = e
148
158
  end
149
159
 
150
160
  # argument types
@@ -223,6 +233,8 @@ module Sneaql
223
233
  r = query_results
224
234
  @logger.debug "adding #{r.length} recs as #{recordset_name}"
225
235
  @recordset_manager.store_recordset(recordset_name, r)
236
+ rescue => e
237
+ @exception_manager.pending_error = e
226
238
  end
227
239
 
228
240
  # argument types
@@ -255,6 +267,8 @@ module Sneaql
255
267
  elsif ((args.size - 1) % 4) == 0
256
268
  iterate_records_conditionally(*args)
257
269
  end
270
+ rescue => e
271
+ @exception_manager.pending_error = e
258
272
  end
259
273
 
260
274
  # custom method for argument validation
@@ -295,6 +309,9 @@ module Sneaql
295
309
  rows_affected_current_statement(tmp)
296
310
  )
297
311
  end
312
+ rescue => e
313
+ @exception_manager.pending_error = e
314
+ raise e
298
315
  end
299
316
 
300
317
  # @param [*Array] args all the arguments passed to the calling function
@@ -312,6 +329,9 @@ module Sneaql
312
329
  rows_affected_current_statement(tmp)
313
330
  )
314
331
  end
332
+ rescue => e
333
+ @exception_manager.pending_error = e
334
+ raise e
315
335
  end
316
336
 
317
337
  # @return [Fixnum] rows affected by the SQL statement
@@ -339,6 +359,111 @@ module Sneaql
339
359
  r.map! { |d| { 'path_name' => d.to_s } }
340
360
  @logger.debug "adding #{r.length} recs as #{recordset_name}"
341
361
  @recordset_manager.store_recordset(recordset_name, r)
362
+ rescue => e
363
+ @exception_manager.pending_error = e
364
+ end
365
+ end
366
+
367
+ # stores all the file paths matching the dir glob into a recordset
368
+ class SneaqlOnError < Sneaql::Core::SneaqlCommand
369
+ Sneaql::Core::RegisterMappedClass.new(
370
+ :command,
371
+ 'on_error',
372
+ Sneaql::Core::Commands::SneaqlOnError
373
+ )
374
+
375
+ # argument types
376
+ def arg_definition
377
+ [:symbol]
378
+ end
379
+
380
+ # @param [String] recordset_name
381
+ # @param [String] dirglob directory glob with optional wildcards
382
+ def action(handler_action)
383
+ # only take action if there is an error
384
+ if @exception_manager.pending_error
385
+ case handler_action.downcase
386
+ when 'continue' then
387
+ @logger.error("#{@exception_manager.pending_error.message}")
388
+ if @exception_manager.pending_error.backtrace
389
+ @exception_manager.pending_error.backtrace.each {|b| @logger.debug(b) }
390
+ end
391
+ @exception_manager.pending_error = nil
392
+ @exception_manager.last_iterated_record = nil
393
+ @logger.info("continuing after error due to on_error handling")
394
+
395
+ when 'exit_step' then
396
+ @logger.error("#{@exception_manager.pending_error.message}")
397
+ if @exception_manager.pending_error.backtrace
398
+ @exception_manager.pending_error.backtrace.each {|b| @logger.debug(b) }
399
+ end
400
+ @exception_manager.pending_error = nil
401
+ @exception_manager.last_iterated_record = nil
402
+
403
+ @logger.info("exiting step due to on_error handling")
404
+ raise Sneaql::Exceptions::SQLTestStepExitCondition
405
+
406
+ when 'execute' then
407
+ @logger.error("#{@exception_manager.pending_error.message}")
408
+ if @exception_manager.pending_error.backtrace
409
+ @exception_manager.pending_error.backtrace.each {|b| @logger.debug(b) }
410
+ end
411
+ @logger.info("executing sql block due to on_error handling")
412
+
413
+ @expression_handler.set_session_variable(
414
+ 'last_statement_rows_affected',
415
+ rows_affected
416
+ )
417
+
418
+ @exception_manager.pending_error = nil
419
+ @exception_manager.last_iterated_record = nil
420
+ end
421
+ end
422
+ end
423
+
424
+ def rows_affected
425
+ tmp = @statement
426
+ tmp = replace_last_record(tmp)
427
+ tmp = replace_error_details(tmp)
428
+ JDBCHelpers::Execute.new(
429
+ @jdbc_connection,
430
+ tmp,
431
+ @logger
432
+ ).rows_affected
433
+ end
434
+
435
+ # replaces a string with references to the field values in the last
436
+ # record iterated (if present). use :err_record.field_name syntax
437
+ # @param [String] input_string containing potential :err_record references
438
+ # @return [String] string with :err_record references replaced
439
+ def replace_last_record(input_string)
440
+ tmp = input_string
441
+ if @exception_manager.last_iterated_record != nil
442
+ @exception_manager.last_iterated_record.keys.sort.reverse.each do |k|
443
+ puts "replacing #{k}"
444
+ tmp = tmp.gsub(
445
+ ":err_record.#{k}",
446
+ @exception_manager.last_iterated_record[k].to_s
447
+ )
448
+ end
449
+ end
450
+ tmp
451
+ end
452
+
453
+ # replaces text `:err_message` and `:err_type` with appropriate
454
+ # values from the pending error
455
+ # @param [String] input_string string containing potential err detail references
456
+ # @return [String] string with err details replaced
457
+ def replace_error_details(input_string)
458
+ tmp = input_string
459
+ tmp = tmp.gsub(
460
+ ':err_message',
461
+ @exception_manager.pending_error.message.to_s
462
+ )
463
+ tmp = tmp.gsub(
464
+ ':err_type',
465
+ @exception_manager.pending_error.class.to_s
466
+ )
342
467
  end
343
468
  end
344
469
  end
@@ -7,7 +7,7 @@ module Sneaql
7
7
  return d[:text] if jdbc_url.match(d[:text])
8
8
  end
9
9
  end
10
-
10
+
11
11
  # Manages preferences for a specific RDBMS implementation.
12
12
  class DatabaseManager
13
13
  attr_reader(
@@ -19,7 +19,7 @@ module Sneaql
19
19
  :commit_statement,
20
20
  :rollback_statement
21
21
  )
22
-
22
+
23
23
  # @param [Hash] options values to override defaults
24
24
  def initialize(options = {})
25
25
  @has_boolean = options.fetch(:has_boolean, default_has_boolean)
@@ -30,17 +30,17 @@ module Sneaql
30
30
  @commit_statement = options.fetch(:commit_statement, default_commit_statement)
31
31
  @rollback_statement = options.fetch(:rollback_statement, default_rollback_statement)
32
32
  end
33
-
33
+
34
34
  # @return [Boolean]
35
35
  def default_has_boolean
36
36
  false
37
37
  end
38
-
38
+
39
39
  # @return [String]
40
40
  def default_autocommit_off_statement
41
41
  nil
42
42
  end
43
-
43
+
44
44
  # @return [Boolean]
45
45
  def default_supports_transactions
46
46
  true
@@ -50,7 +50,7 @@ module Sneaql
50
50
  def default_supports_table_locking
51
51
  false
52
52
  end
53
-
53
+
54
54
  # @return [String] begin statement
55
55
  def default_begin_statement
56
56
  "begin;"
@@ -60,12 +60,12 @@ module Sneaql
60
60
  def default_commit_statement
61
61
  "commit;"
62
62
  end
63
-
63
+
64
64
  # @return [String] rollback statement
65
65
  def default_rollback_statement
66
66
  "rollback;"
67
67
  end
68
-
68
+
69
69
  # @param [String] table_name
70
70
  # @return [String] rollback statement
71
71
  def lock_table_statement(table_name)
@@ -6,17 +6,16 @@ module Sneaql
6
6
  :database,
7
7
  'redshift',
8
8
  Sneaql::Core::RedshiftDatabaseManager
9
- )
10
-
11
- def initialize()
9
+ )
10
+
11
+ def initialize
12
12
  super(
13
13
  {
14
14
  has_boolean: true,
15
15
  autocommit_off_statement: 'set autocommit=off;'
16
16
  }
17
- )
17
+ )
18
18
  end
19
-
20
19
  end
21
20
  end
22
- end
21
+ end
@@ -6,7 +6,7 @@ module Sneaql
6
6
  :database,
7
7
  'sqlite',
8
8
  Sneaql::Core::SqliteDatabaseManager
9
- )
9
+ )
10
10
  end
11
11
  end
12
- end
12
+ end
@@ -6,16 +6,16 @@ module Sneaql
6
6
  :database,
7
7
  'vertica',
8
8
  Sneaql::Core::VerticaDatabaseManager
9
- )
10
-
9
+ )
10
+
11
11
  def initialize
12
12
  super(
13
13
  {
14
14
  has_boolean: true,
15
15
  autocommit_off_statement: 'set session autocommit to off;'
16
16
  }
17
- )
17
+ )
18
18
  end
19
19
  end
20
20
  end
21
- end
21
+ end
@@ -3,26 +3,26 @@ require 'json'
3
3
  module Sneaql
4
4
  module Docker
5
5
  class LocalTransformDockerfile
6
-
6
+
7
7
  def initialize(repo_dir, repo_tag)
8
8
  @repo_dir = repo_dir
9
9
  @repo_tag = repo_tag
10
10
  @steps = JSON.parse(
11
11
  File.read("#{repo_dir}/sneaql.json")
12
12
  )
13
-
13
+
14
14
  create_step_files
15
15
  create_dockerfile
16
16
  end
17
-
18
- def create_step_files()
17
+
18
+ def create_step_files
19
19
  @step_files = []
20
20
  @step_files << {
21
21
  docker_path: 'sneaql.json',
22
22
  local_path: 'sneaql.json' #File.expand_path("#{repo_dir}/sneaql.json")
23
23
  }
24
-
25
- @steps.each { |s|
24
+
25
+ @steps.each { |s|
26
26
  @step_files << {
27
27
  docker_path: s['step_file'],
28
28
  local_path: s['step_file'] # File.expand_path(s['step_file'])
@@ -30,9 +30,9 @@ module Sneaql
30
30
  }
31
31
  end
32
32
 
33
- def dockerfile()
33
+ def dockerfile
34
34
  %{FROM full360/sneaql:latest
35
- RUN mkdir /repo
35
+ RUN mkdir /repo
36
36
  #{@step_files.map {|s| "ADD #{s[:local_path]} /repo/#{s[:docker_path]}"}.join("\n")}
37
37
  }
38
38
  end
@@ -56,4 +56,4 @@ RUN mkdir /repo
56
56
  end
57
57
  end
58
58
  end
59
- end
59
+ end
@@ -1,19 +1,39 @@
1
1
  module Sneaql
2
2
  # Exceptions for SneaQL
3
3
  module Exceptions
4
+ class ExceptionManager
5
+ attr_accessor :pending_error
6
+ attr_accessor :last_iterated_record
7
+
8
+ def initialize(logger = nil)
9
+ @logger = logger ? logger : Logger.new(STDOUT)
10
+ end
11
+
12
+ def output_pending_error
13
+ @logger.error "pending error: #{@pending_error}" if @pending_error
14
+ end
15
+ end
16
+
4
17
  # Base error class for Sneaql
5
18
  class BaseError < StandardError; end
19
+
20
+ # Exception used to to gracefully exit test
21
+ class UnhandledException < BaseError
22
+ def initialize(msg = 'Previous error was not handled.')
23
+ super
24
+ end
25
+ end
6
26
 
7
27
  # Exception used to to gracefully exit test
8
28
  class SQLTestExitCondition < BaseError
9
- def initialize(msg = "Exit condition met by test, this is not an error")
29
+ def initialize(msg = 'Exit condition met by test, this is not an error')
10
30
  super
11
31
  end
12
32
  end
13
-
33
+
14
34
  # Exception used to gracefully exit test step
15
35
  class SQLTestStepExitCondition < BaseError
16
- def initialize(msg = "Exit condition for this step has been met, this is not an error")
36
+ def initialize(msg = 'Exit condition for this step has been met, this is not an error')
17
37
  super
18
38
  end
19
39
  end
@@ -21,56 +41,56 @@ module Sneaql
21
41
  # Transform is locked by another process. This is a
22
42
  # possibility when using the LockManager
23
43
  class TransformIsLocked < BaseError
24
- def initialize(msg = "This transform is locked by another process")
44
+ def initialize(msg = 'This transform is locked by another process')
25
45
  super
26
46
  end
27
47
  end
28
48
 
29
49
  # Recordset check failure indicator
30
50
  class RecordsetContainsInconsistentOrInvalidTypes < BaseError
31
- def initialize(msg = "Recordsets must have identical keys in every record")
51
+ def initialize(msg = 'Recordsets must have identical keys in every record')
32
52
  super
33
53
  end
34
54
  end
35
-
55
+
36
56
  # Recordset check failure indicator
37
57
  class RecordsetIsNotAnArray < BaseError
38
- def initialize(msg = "Recordset must be an array of hashes with identical keys")
58
+ def initialize(msg = 'Recordset must be an array of hashes with identical keys')
39
59
  super
40
60
  end
41
61
  end
42
62
 
43
- # General error evaluating expression.
63
+ # General error evaluating expression.
44
64
  class ExpressionEvaluationError < BaseError
45
- def initialize(msg = "Error evaluating expression")
65
+ def initialize(msg = 'Error evaluating expression')
46
66
  super
47
67
  end
48
68
  end
49
-
69
+
50
70
  # Comparison operator must be explicitly supported.
51
71
  class InvalidComparisonOperator < BaseError
52
- def initialize(msg = "Invalid or no comparison operator provided")
72
+ def initialize(msg = 'Invalid or no comparison operator provided')
53
73
  super
54
74
  end
55
75
  end
56
-
76
+
57
77
  # Error raised during parser validation process
58
78
  class StatementParsingError < BaseError
59
- def initialize(msg = "General error parsing Sneaql tag and statement")
79
+ def initialize(msg = 'General error parsing Sneaql tag and statement')
60
80
  super
61
81
  end
62
82
  end
63
-
83
+
64
84
  # Sneaql step files must not be empty
65
85
  class NoStatementsFoundInFile < BaseError
66
- def initialize(msg = "No statements found in step file.")
86
+ def initialize(msg = 'No statements found in step file.')
67
87
  super
68
88
  end
69
89
  end
70
90
 
71
91
  # Sneaql command tags must be formed correctly
72
92
  class MalformedSneaqlCommandsInStep < BaseError
73
- def initialize(msg = "Sneaql command tag is malformed.")
93
+ def initialize(msg = 'Sneaql command tag is malformed.')
74
94
  super
75
95
  end
76
96
  end