sneaql 0.0.13-java → 0.0.15-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 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