sqlsnip 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/exe/sqlsnip +57 -18
  3. data/lib/sqlsnip/version.rb +1 -1
  4. data/lib/sqlsnip.rb +101 -60
  5. metadata +30 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4691a62621199c780a25a27c14467e10ae3b48e2a20961a6f5660d5deab6b0aa
4
- data.tar.gz: 7077d2b840173151db73d7bf038974791bbb5c4319e098d9741c2ef0b0b193b7
3
+ metadata.gz: eff6793c0897ff170d7113e76b9599ebba9e781a17a7c79f0e290072f51b8126
4
+ data.tar.gz: 18cb83ef312352f398989ad01f96bff419db7cf755d1c93d891bb9c8cc641c08
5
5
  SHA512:
6
- metadata.gz: 4b9f2117d5242c5535a434e036c0a0926f399c3ef0cb31d6deac57f8c187048900cbba9d5d06f649fcd9bbebff95e8e172efab9713262f7422fb3157269c1b6d
7
- data.tar.gz: 455d2f47f9be97fdc91e5d8b0f909a0846e19af94c3ad028550c2428373aee4c8febf923ad35743916b0aadc2d4ce544effebb491b568427744fc196fd27ffa6
6
+ metadata.gz: 2b28921c49c85b91418e74a5e193431187778fa84a274c5a3d894b03fc0269da4cec30955d551123eb1451f7af325cf67115b75068a604cbae38b290f71e5e09
7
+ data.tar.gz: 1836d49c1dc2f46f4889d92243b2b59452017ccabb68af0ed261ab62b2d5990ff0a0bee7627b4580a219b1a6e13e81fc5d117b6f6ad2364b0625fece9fd47fc8
data/exe/sqlsnip CHANGED
@@ -11,11 +11,11 @@ SPEC = %(
11
11
  -- FILE [START [STOP]]
12
12
 
13
13
  Prepares a range of lines in a SQL file for execution by adding drop
14
- statements for object defined in the range. If the range is absent, the whole
15
- file is executed. Prints the resulting SQL script on standard output so it
16
- can be piped to psql(1)
14
+ statements for objects defined in the range. If the range is absent, the
15
+ whole file is executed. Prints the resulting SQL script on standard output so
16
+ it can be piped to psql(1)
17
17
 
18
- The range is given by a start and a stop line number (inclusive) and is
18
+ The range is given by a start and a stop line number (inclusive). It is
19
19
  scanned for lines matching 'create' and a drop statement is added for each
20
20
  match. Table, view, function, procedure, and trigger objects are supported.
21
21
  'or replace' definitions does not generate drop statements
@@ -28,37 +28,76 @@ SPEC = %(
28
28
  Note that the parser is very primitive: It expects the header of the
29
29
  definition to be on one line. It also doesn't know about comments
30
30
 
31
+ -i,interactive
32
+ Emit PSQL session statements (ON_ERROR_STOP)
33
+
34
+ -s,schema=SCHEMA?
35
+ Set initial search path. If SCHEMA is absent no initial search path is
36
+ emitted. Explicit search path settings in the source are always emitted
37
+
38
+ -d,drop-only
39
+ Only emit drop-statements. Can be used to insert drop statements in the
40
+ current editor
41
+
31
42
  EXAMPLE
32
43
 
33
44
  sqlsnip can be used to create a vim macro in .vimrcc that executes the
34
45
  selected text or the whole file:
35
46
 
36
- \\function! ExecuteSqlFile() range
47
+ \\function! PrepareSqlRange() range
37
48
  let path = expand('%:p')
38
- let result = system('sqlsnip' . path . ' | psql')
49
+ let start_line = line("'<")
50
+ let end_line = line("'>")
51
+ let result = system('sqlsnip ' . path . ' ' . start_line . ' ' . end_line . ' | psql')
39
52
  echo result
40
53
  endfunction
41
54
 
42
- function! ExecuteSqlRange() range
55
+ " CTRL-X - execute selected range
56
+ map <C-X> :call PrepareSqlRange()<CR>
57
+
58
+ ...or the whole file
59
+
60
+ \\function! PrepareSqlFile() range
43
61
  let path = expand('%:p')
44
- let start_line = line("'<")
45
- let end_line = line("'>")
46
- let result = system('sqlsnip ' . path . ' ' . start_line . ' ' . end_line . ' | psql')
62
+ let result = system('sqlsnip' . path . ' | psql')
47
63
  echo result
48
64
  endfunction
49
65
 
50
66
  " CTRL+G - execute whole file
51
- map <C-G> :call ExecuteSqlFile()<CR>
67
+ map <C-G> :call PrepareSqlFile()<CR>
68
+
69
+ ...or to just replace the current selected text with drop statements
70
+
71
+ \\function! ReplaceSqlRange() range " ChatGPT
72
+ let start_line = line("'<")
73
+ let end_line = line("'>")
74
+ let save_cursor = getpos('.')
75
+ let range_text = getline(start_line, end_line)
76
+ let path = expand('%:p')
77
+ let shell_output = system('bundle exec exe/sqlsnip -ds"" ' . path)
78
+ let shell_output_lines = split(shell_output, "\n")
79
+ call deletebufline('%', start_line, end_line)
80
+ call append(start_line - 1, shell_output_lines)
81
+ call setpos('.', save_cursor)
82
+ endfunction
83
+
84
+ " CTRL+I - insert drop statements
85
+ map <C-I> :call ReplaceSqlRange()<CR>
52
86
 
53
- " CTRL-X - execute selected range
54
- map <C-X> :call ExecuteSqlRange()<CR>
55
87
  )
56
88
 
57
89
  opts, args = ShellOpts.process(SPEC, ARGV)
58
- file, start_line, stop_line = args.extract(1..3)
59
- start_line ||= "1"
60
- stop_line ||= "100000000"
90
+ file, start_line, stop_line = args.expect(1..3)
91
+ start_line &&= start_line.to_i
92
+ stop_line &&= stop_line.to_i
93
+
94
+ search_path = opts.schema? ? opts.schema || "" : nil
95
+
96
+ source = Sqlsnip::Source.parse(file, start_line, stop_line, search_path: search_path)
97
+ puts source.generate(interactive: opts.interactive?)
61
98
 
62
- prg = Sqlsnip::Prg.new(file, start_line.to_i, stop_line.to_i)
63
- prg.run
99
+ if !opts.drop_only?
100
+ puts
101
+ puts source.lines
102
+ end
64
103
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sqlsnip
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/sqlsnip.rb CHANGED
@@ -1,11 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'string-text'
4
+ require 'constrain'
5
+ include Constrain
6
+
3
7
  require_relative "sqlsnip/version"
4
8
 
9
+
5
10
  module Sqlsnip
6
11
  class Error < StandardError; end
7
12
 
8
- class Prg
13
+ class Source
9
14
  UID_RE = /(?:[.\w]+)/
10
15
  TABLE_MODIFIERS_RE = /(?:global|local|temporary|temp|unlogged)/
11
16
  VIEW_MODIFIERS_RE = /(?:temp|temporary|recursive)/
@@ -27,80 +32,57 @@ module Sqlsnip
27
32
 
28
33
  SEARCH_PATH_RE = /^\s*set\s+search_path/
29
34
 
30
- attr_reader :file, :start_line, :stop_line
31
- attr_reader :lines # The selected range of lines
35
+ # Source file
36
+ attr_reader :file
37
+
38
+ # Starting and ending line (inclusive). May be nil
39
+ attr_reader :start_line, :stop_line
40
+
41
+ # The selected range of lines as read from the file
42
+ attr_reader :lines
43
+
44
+ # Array of generated statements
45
+ attr_reader :stmts
32
46
 
33
- def initialize(file, start_line, stop_line)
47
+ # Initial search path
48
+ attr_reader :search_path
49
+
50
+ def initialize(file, start_line = nil, stop_line = nil, search_path: nil)
51
+ constrain file, String
52
+ constrain start_line, Integer, nil
53
+ constrain stop_line, Integer, nil
54
+ constrain search_path, String, nil
34
55
  @file, @start_line, @stop_line = file, start_line, stop_line
35
56
  File.exist?(@file) or raise Error, "Can't find #{file}"
36
57
  @lines = []
37
- @initial_search_path = nil
58
+ @stmts = nil
59
+ @search_path = search_path
60
+ if @search_path && !@search_path.empty?
61
+ @search_path_stmt = "set search_path to #{@search_path};"
62
+ end
38
63
  @project_dir = nil
39
64
  end
40
65
 
41
- def run
66
+ def parse
42
67
  read_lines
43
- stmts, has_search_path = generate_drop_statements
44
- if !has_search_path
45
- search_path = @initial_search_path
46
- if search_path.nil?
47
- schema = find_schema_from_file(file)
48
- search_path = "set search_path to #{schema}"
49
- end
50
- stmts.unshift search_path
51
- end
52
-
53
- puts '\set ON_ERROR_STOP on'
54
- puts stmts
55
- puts
56
- puts lines
57
- end
58
-
59
- private
60
- attr_reader :initial_search_path, :project_dir
61
-
62
- def read_lines
63
- IO.readlines(file).each.with_index { |line, i|
64
- i += 1
65
- if i < start_line
66
- @initial_search_path = line if line =~ /^\s*set\s+search_path/
67
- elsif i <= stop_line
68
- lines << line
69
- else
70
- break
71
- end
72
- }
68
+ generate_drop_stmts
69
+ self
73
70
  end
74
71
 
75
- def find_project_dir(path)
76
- path = File.absolute_path(path)
77
- while !File.exist?(File.join(path, "prick.yml"))
78
- path != "/" or raise Error, "Can't find project directory"
79
- path = File.dirname(path)
80
- end
81
- path
82
- end
72
+ def self.parse(*args, **opts) self.new(*args, **opts).parse end
83
73
 
84
- def find_schema_from_file(file)
85
- path = File.dirname(file)
86
- project_dir = find_project_dir(File.dirname(file))
87
- path = path.delete_prefix(project_dir)
88
- if path =~ /schema\/([^\/]+)/
89
- schema = $1
90
- else
91
- schema != "" or raise Error, "Can't find schema from #{file}"
92
- end
93
- schema
74
+ def generate(interactive: false)
75
+ generate_search_path_stmt if @search_path != ""
76
+ generate_interactive_stmts if interactive
77
+ @stmts
94
78
  end
95
79
 
96
- def generate_drop_statements
97
- has_search_path = false
98
- stmts = []
80
+ def generate_drop_stmts
81
+ @stmts = []
99
82
  for line in lines
100
83
  case line
101
84
  when /^\s*set\s+search_path/
102
85
  sql = line
103
- has_search_path = true if stmts.empty?
104
86
  when /^\s*create\s+(.*)/
105
87
  object = $1
106
88
  case object
@@ -134,9 +116,68 @@ module Sqlsnip
134
116
  else
135
117
  next
136
118
  end
137
- stmts << sql
119
+ @stmts << sql
138
120
  end
139
- [stmts, has_search_path]
121
+ end
122
+
123
+ # Generate a 'set search_path' statement
124
+ def generate_search_path_stmt
125
+ if @search_path == ""
126
+ return @stmts
127
+ elsif @search_path_stmt
128
+ @stmts.unshift @search_path_stmt
129
+ else
130
+ schema = find_schema_from_file(file)
131
+ search_path = "set search_path to #{schema};"
132
+ @stmts.unshift search_path
133
+ end
134
+ end
135
+
136
+ def generate_interactive_stmts
137
+ @stmts.unshift '\set ON_ERROR_STOP on'
138
+ end
139
+
140
+
141
+ private
142
+ attr_reader :search_path_stmt, :project_dir
143
+
144
+ def read_lines
145
+ IO.readlines(file).each.with_index { |line, i|
146
+ line.chomp!
147
+ i += 1
148
+ break if !stop_line.nil? && i > stop_line
149
+ next if line =~ /^\s*$/
150
+ if @lines.empty? && @search_path.nil? && line =~ /^\s*set\s+search_path/
151
+ @search_path_stmt = line
152
+ elsif !start_line.nil? && i < start_line
153
+ ;
154
+ elsif stop_line.nil? || i <= stop_line
155
+ @lines << line
156
+ end
157
+ }
158
+ end
159
+
160
+ # Search upwards in the directory hierarchy for a prick project directory
161
+ def find_project_dir(path)
162
+ path = File.absolute_path(path)
163
+ while !File.exist?(File.join(path, "prick.yml"))
164
+ path != "/" or raise Error, "Can't find project directory"
165
+ path = File.dirname(path)
166
+ end
167
+ path
168
+ end
169
+
170
+ # Use the prick source directory structure to find the schema name
171
+ def find_schema_from_file(file)
172
+ path = File.dirname(file)
173
+ project_dir = find_project_dir(File.dirname(file))
174
+ path = path.delete_prefix(project_dir)
175
+ if path =~ /schema\/([^\/]+)/
176
+ schema = $1
177
+ else
178
+ schema != "" or raise Error, "Can't find schema from #{file}"
179
+ end
180
+ schema
140
181
  end
141
182
  end
142
183
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqlsnip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claus Rasmussen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-05-19 00:00:00.000000000 Z
11
+ date: 2024-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: indented_io
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: string-text
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: constrain
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  description: Gem sqlsnip
42
70
  email:
43
71
  - claus.l.rasmussen@gmail.com