sqlsnip 0.1.0 → 0.2.1

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.
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