pg_exec_array_params 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/Gemfile +6 -2
- data/README.md +33 -6
- data/lib/pg_exec_array_params.rb +18 -1
- data/lib/pg_exec_array_params/column.rb +32 -0
- data/lib/pg_exec_array_params/error.rb +18 -0
- data/lib/pg_exec_array_params/query.rb +27 -92
- data/lib/pg_exec_array_params/rewriters.rb +6 -0
- data/lib/pg_exec_array_params/rewriters/a_expr.rb +80 -0
- data/lib/pg_exec_array_params/rewriters/node.rb +35 -0
- data/lib/pg_exec_array_params/rewriters/res_target.rb +42 -0
- data/lib/pg_exec_array_params/sql_ref_index.rb +31 -0
- data/lib/pg_exec_array_params/version.rb +1 -1
- data/pg_exec_array_params-0.1.0.gem +0 -0
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1fdcb04ffde2b46c124f9165cfb0a383e15583d01ae52c830211e51894c44a78
|
4
|
+
data.tar.gz: 410af6e18d74d9feab49848f1796831ac79aefadfccd40614b40743abbacf727
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d7f4877366134724817ff915786446d622657d40c820d2b3f1fb31dd3237862366b840c6c0ab483760ad91918b4c238d41db0c89db3301ac981381b032849867
|
7
|
+
data.tar.gz: ccf1a880c9442b5447cd43a43b262e507503b126d0854e21539ec79927a5fb17c5257f6f5d2995d9ac5cde74e3119c45fc8a9dfd1890d6825c7d368bffb02b97
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
@@ -5,11 +5,15 @@ source 'https://rubygems.org'
|
|
5
5
|
# Specify your gem's dependencies in pg_exec_array_params.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
gem 'rails'
|
9
|
+
|
8
10
|
gem 'codecov', require: false
|
11
|
+
gem 'pry-byebug', '~> 3.9'
|
12
|
+
gem 'rake', '~> 12.0'
|
13
|
+
gem 'rspec-github', '~> 2.3'
|
14
|
+
gem 'rspec-its', '~> 1.3'
|
9
15
|
gem 'rubocop', '~> 1.0'
|
10
16
|
gem 'rubocop-rspec', '~> 2.0.0.pre'
|
11
|
-
gem 'rspec-github', '~> 2.3'
|
12
|
-
gem 'pry-byebug', '~> 3.9'
|
13
17
|
gem 'simplecov', '~> 0.19'
|
14
18
|
|
15
19
|
group :benchmark do
|
data/README.md
CHANGED
@@ -1,21 +1,38 @@
|
|
1
1
|
# PgExecArrayParams
|
2
2
|
|
3
3
|
![](https://github.com/lunatic-cat/pg_exec_array_params/workflows/ci/badge.svg)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/pg_exec_array_params.svg)](https://badge.fury.io/rb/pg_exec_array_params)
|
5
|
+
[![codecov](https://codecov.io/gh/lunatic-cat/pg_exec_array_params/branch/master/graph/badge.svg?token=X5K67X3V0Z)](undefined)
|
4
6
|
|
5
7
|
Use same parametized query and put `Array<T>` instead of any `T`
|
6
8
|
|
7
9
|
## Example
|
8
10
|
|
9
|
-
|
10
|
-
query = 'select * from t1 where a1 = $1 and a2 = $2 and a3 = $3 and a4 = $4'
|
11
|
-
params = [1, [2, 3], 'foo', ['bar', 'baz']]
|
11
|
+
### Inside `WHERE` part
|
12
12
|
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
13
|
+
```ruby
|
14
|
+
# Instead of:
|
15
|
+
# PG::Connection.exec_params(
|
16
|
+
# 'SELECT * FROM "t1" WHERE "a1" = $1 AND "a3" IN ($4, $5, $6) AND "a2" IN ($2, $3)',
|
17
|
+
# [1, 2, 3, "foo", "bar", "baz"]
|
18
|
+
# )
|
19
|
+
query = 'select * from t1 where a1 = $1 and a3 = $3 and a2 = $2'
|
20
|
+
params = [1, [2, 3], ['foo', 'bar', 'baz']]
|
16
21
|
PgExecArrayParams.exec_array_params(conn, query, params)
|
17
22
|
```
|
18
23
|
|
24
|
+
### Inside `SELECT` part
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
# Instead of:
|
28
|
+
# PG::Connection.exec_params(
|
29
|
+
# 'SELECT ARRAY[$1, $2]'
|
30
|
+
# [1, 2]
|
31
|
+
# )
|
32
|
+
PgExecArrayParams.exec_array_params(conn, 'select $1', [[1, 2]])
|
33
|
+
=> [{"array"=>"{1,2}"}]
|
34
|
+
```
|
35
|
+
|
19
36
|
## Problem
|
20
37
|
|
21
38
|
```ruby
|
@@ -40,6 +57,16 @@ PgExecArrayParams.exec_array_params(conn, 'select * from users where id = $1', [
|
|
40
57
|
=> [{"id" => 1}, {"id" => 2}]
|
41
58
|
```
|
42
59
|
|
60
|
+
## Batteries
|
61
|
+
|
62
|
+
This can also provide more info than plain `pg_query` gem:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
sql = 'with y as (select * from s) select x1, y.y1, z.z as z1 from x join z on z.z = x join y on y.y = x'
|
66
|
+
PgExecArrayParams::Query.new(sql, []).columns.map(&:name)
|
67
|
+
=> ['x1', 'y1', 'z1']
|
68
|
+
```
|
69
|
+
|
43
70
|
## Integration with 'pg' gem
|
44
71
|
|
45
72
|
```ruby
|
data/lib/pg_exec_array_params.rb
CHANGED
@@ -1,10 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'pg_exec_array_params/error'
|
4
|
+
require 'pg_exec_array_params/rewriters'
|
5
|
+
require 'pg_exec_array_params/rewriters/node'
|
6
|
+
require 'pg_exec_array_params/rewriters/res_target'
|
7
|
+
require 'pg_exec_array_params/rewriters/a_expr'
|
8
|
+
require 'pg_exec_array_params/sql_ref_index'
|
9
|
+
require 'pg_exec_array_params/column'
|
3
10
|
require 'pg_exec_array_params/query'
|
4
11
|
require 'pg_exec_array_params/version'
|
5
12
|
|
6
13
|
module PgExecArrayParams
|
7
|
-
|
14
|
+
PARAM_REF = 'ParamRef'
|
15
|
+
REXPR = 'rexpr'
|
16
|
+
NUMBER = 'number'
|
17
|
+
LOCATION = 'location'
|
18
|
+
|
19
|
+
# AExpr['kind']
|
20
|
+
EQ_KIND = 0
|
21
|
+
IN_KIND = 7
|
22
|
+
|
23
|
+
class Optional; end
|
24
|
+
|
8
25
|
module_function
|
9
26
|
|
10
27
|
def exec_array_params(conn, sql, params, *args)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgExecArrayParams
|
4
|
+
class Column
|
5
|
+
attr_reader :table, :column_name, :as_name
|
6
|
+
|
7
|
+
def initialize(table:, column_name:, as_name:)
|
8
|
+
@table = table
|
9
|
+
@column_name = column_name
|
10
|
+
@as_name = as_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
@as_name || @column_name
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_res_target(res_target)
|
18
|
+
return unless (column_ref = res_target.fetch('val', {})['ColumnRef'])
|
19
|
+
|
20
|
+
idents = column_ref['fields'].map { |field| field.fetch('String', {})['str'] }
|
21
|
+
if idents.size <= 1
|
22
|
+
column_name = idents.first
|
23
|
+
else
|
24
|
+
table, column_name, = idents
|
25
|
+
end
|
26
|
+
|
27
|
+
return unless column_name
|
28
|
+
|
29
|
+
new(table: table, column_name: column_name, as_name: res_target['name'])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgExecArrayParams
|
4
|
+
class Error < StandardError
|
5
|
+
attr_accessor :query, :node
|
6
|
+
|
7
|
+
def initialize(message, query = nil, node = nil)
|
8
|
+
super(message)
|
9
|
+
@msg = message
|
10
|
+
@query = query
|
11
|
+
@node = node
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"#{@msg}\n#{@query}\n#{@node}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -4,16 +4,6 @@ require 'pg_query'
|
|
4
4
|
|
5
5
|
module PgExecArrayParams
|
6
6
|
class Query
|
7
|
-
PARAM_REF = 'ParamRef'
|
8
|
-
REXPR = 'rexpr'
|
9
|
-
A_EXPR = 'A_Expr'
|
10
|
-
KIND = 'kind'
|
11
|
-
LOCATION = 'location'
|
12
|
-
NUMBER = 'number'
|
13
|
-
|
14
|
-
EQ_KIND = 0
|
15
|
-
IN_KIND = 7
|
16
|
-
|
17
7
|
attr_reader :query, :args
|
18
8
|
|
19
9
|
def initialize(query, args = [])
|
@@ -26,109 +16,54 @@ module PgExecArrayParams
|
|
26
16
|
end
|
27
17
|
|
28
18
|
def sql
|
29
|
-
|
30
|
-
|
31
|
-
@sql || (rebuild_query! && @sql)
|
19
|
+
should_rebuild? ? (@sql || (rebuild_query! && @sql)) : query
|
32
20
|
end
|
33
21
|
|
34
22
|
def binds
|
35
|
-
|
23
|
+
should_rebuild? ? (@binds ||= args.flatten(1)) : args
|
24
|
+
end
|
36
25
|
|
37
|
-
|
26
|
+
def columns
|
27
|
+
@columns || (rebuild_query! && @columns)
|
38
28
|
end
|
39
29
|
|
40
30
|
private
|
41
31
|
|
42
32
|
def should_rebuild?
|
43
|
-
args.any?
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@param_idx = 0
|
48
|
-
@ref_idx = 1
|
49
|
-
@binds = []
|
50
|
-
each_param_ref do |value|
|
51
|
-
# puts({value_before: value}.inspect)
|
52
|
-
|
53
|
-
if args[@param_idx].is_a? Array
|
54
|
-
value[KIND] = IN_KIND
|
55
|
-
value[REXPR] = []
|
56
|
-
args[@param_idx].each do |param|
|
57
|
-
raise Error, "Param: #{param.inspect} not primitive" if param.respond_to?(:each)
|
58
|
-
|
59
|
-
value[REXPR] << { PARAM_REF => { NUMBER => @ref_idx } }
|
60
|
-
@binds << param
|
61
|
-
@ref_idx += 1
|
62
|
-
end
|
63
|
-
else
|
64
|
-
value[REXPR][PARAM_REF][NUMBER] = @ref_idx
|
65
|
-
@ref_idx += 1
|
66
|
-
|
67
|
-
# nested_refs == 1 unwraps, wrap it back
|
68
|
-
value[REXPR] = [value[REXPR]] if value[KIND] == IN_KIND
|
69
|
-
|
70
|
-
@binds << args[@param_idx]
|
71
|
-
end
|
72
|
-
|
73
|
-
@param_idx += 1
|
74
|
-
# puts({value_after_: value}.inspect)
|
33
|
+
args.any? do |param|
|
34
|
+
param.is_a?(Array) && (param.none? do |item|
|
35
|
+
item.respond_to?(:each) && raise(Error, "Param includes not primitive: #{item.inspect}")
|
36
|
+
end)
|
75
37
|
end
|
76
|
-
@sql = tree.deparse
|
77
|
-
# puts({sql: @sql, binds: @binds}.inspect)
|
78
|
-
true
|
79
38
|
end
|
80
39
|
|
81
40
|
def tree
|
82
41
|
@tree ||= PgQuery.parse(query)
|
83
42
|
end
|
84
43
|
|
85
|
-
def
|
44
|
+
def rebuild_query!
|
45
|
+
@columns ||= []
|
86
46
|
tree.send :treewalker!, tree.tree do |_expr, key, value, _location|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
'Cannot splice multiple references, leave the only one:',
|
97
|
-
query,
|
98
|
-
refs_underline(value)
|
99
|
-
].join("\n")
|
100
|
-
raise Error, message
|
101
|
-
end
|
102
|
-
end
|
47
|
+
case key
|
48
|
+
when 'targetList'
|
49
|
+
@columns += value.map do |node|
|
50
|
+
Column.from_res_target(node['ResTarget'])
|
51
|
+
end.compact
|
52
|
+
when 'ResTarget'
|
53
|
+
Rewriters::ResTarget.new(value, ref_idx).process
|
54
|
+
when 'A_Expr'
|
55
|
+
Rewriters::AExpr.new(value, ref_idx).process
|
103
56
|
end
|
104
57
|
end
|
58
|
+
@sql = tree.deparse
|
59
|
+
true
|
60
|
+
rescue Error => e
|
61
|
+
e.query = query
|
62
|
+
raise e
|
105
63
|
end
|
106
64
|
|
107
|
-
def
|
108
|
-
|
109
|
-
"#{'^'.rjust(from, ' ')}#{'-'.rjust(size, '-')}^"
|
110
|
-
end
|
111
|
-
|
112
|
-
def refs_at(value)
|
113
|
-
first_ref = value[REXPR].find { |vexpr| vexpr.key?(PARAM_REF) } [PARAM_REF]
|
114
|
-
last_ref = value[REXPR].reverse.find { |vexpr| vexpr.key?(PARAM_REF) } [PARAM_REF]
|
115
|
-
started = first_ref[LOCATION] + 1
|
116
|
-
ended = last_ref[LOCATION] + last_ref[NUMBER].to_s.size
|
117
|
-
[started, ended - started]
|
118
|
-
end
|
119
|
-
|
120
|
-
# = $1
|
121
|
-
# {"kind"=>0, "name"=>[{"String"=>{"str"=>"="}}],
|
122
|
-
# "lexpr"=>{"ColumnRef"=>{"fields"=>[{"String"=>{"str"=>"companies"}}, {"String"=>{"str"=>"id"}}],
|
123
|
-
# "location"=>1242}},
|
124
|
-
# "rexpr"=>{"ParamRef"=>{"number"=>4, "location"=>1261}}, "location"=>1259}
|
125
|
-
def assign_param_via_eq?(value)
|
126
|
-
(value[KIND] == EQ_KIND) && value[REXPR].is_a?(Hash) && value[REXPR].key?(PARAM_REF)
|
127
|
-
end
|
128
|
-
|
129
|
-
# IN ($1), returns number of nested REFs
|
130
|
-
def assign_param_via_in?(value)
|
131
|
-
(value[KIND] == IN_KIND) && value[REXPR].is_a?(Array) && value[REXPR].count { |vexpr| vexpr.key?(PARAM_REF) }
|
65
|
+
def ref_idx
|
66
|
+
@ref_idx ||= SqlRefIndex.new(args)
|
132
67
|
end
|
133
68
|
end
|
134
69
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgExecArrayParams
|
4
|
+
module Rewriters
|
5
|
+
class AExpr < Node
|
6
|
+
KIND = 'kind'
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def should_rewrite?
|
11
|
+
return true if assign_param_via_eq?
|
12
|
+
|
13
|
+
if (nested_refs = assign_param_via_in?)
|
14
|
+
if nested_refs == 1
|
15
|
+
value[REXPR] = value[REXPR].first
|
16
|
+
return true
|
17
|
+
else
|
18
|
+
suggest_n = value[REXPR].first[PARAM_REF][NUMBER]
|
19
|
+
raise Error.new("Leave only `= $#{suggest_n}` and pass an array", nil, self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def rewrite!
|
26
|
+
# puts({value_before: value}.inspect)
|
27
|
+
old_ref_idx = value[REXPR][PARAM_REF][NUMBER] - 1 # one based
|
28
|
+
unless (new_ref_idx = ref_idx[old_ref_idx])
|
29
|
+
raise Error.new("No parameter for $#{old_ref_idx + 1}", nil, self)
|
30
|
+
end
|
31
|
+
|
32
|
+
if new_ref_idx.is_a?(Array)
|
33
|
+
value[KIND] = IN_KIND
|
34
|
+
value[REXPR] = Range.new(*new_ref_idx).map do |param_ref_idx|
|
35
|
+
{ PARAM_REF => { NUMBER => param_ref_idx } }
|
36
|
+
end
|
37
|
+
else
|
38
|
+
value[REXPR][PARAM_REF][NUMBER] = new_ref_idx
|
39
|
+
# nested_refs == 1 unwraps, wrap it back
|
40
|
+
value[REXPR] = [value[REXPR]] if value[KIND] == IN_KIND
|
41
|
+
end
|
42
|
+
# puts({value_after_: value}.inspect)
|
43
|
+
end
|
44
|
+
|
45
|
+
# = $1
|
46
|
+
# {"kind"=>0, "name"=>[{"String"=>{"str"=>"="}}],
|
47
|
+
# "lexpr"=>{"ColumnRef"=>{"fields"=>[{"String"=>{"str"=>"companies"}}, {"String"=>{"str"=>"id"}}],
|
48
|
+
# "location"=>1242}},
|
49
|
+
# "rexpr"=>{"ParamRef"=>{"number"=>4, "location"=>1261}}, "location"=>1259}
|
50
|
+
def assign_param_via_eq?
|
51
|
+
(value[KIND] == EQ_KIND) && value[REXPR].is_a?(Hash) && value[REXPR].key?(PARAM_REF)
|
52
|
+
end
|
53
|
+
|
54
|
+
# IN ($1), returns number of nested REFs
|
55
|
+
def assign_param_via_in?
|
56
|
+
(value[KIND] == IN_KIND) && value[REXPR].is_a?(Array) && value[REXPR].count { |vexpr| vexpr.key?(PARAM_REF) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def refs_at
|
60
|
+
first_ref = wrap_array(value[REXPR]).find { |vexpr| vexpr.key?(PARAM_REF) }&.fetch(PARAM_REF, {})
|
61
|
+
last_ref = wrap_array(value[REXPR]).reverse.find { |vexpr| vexpr.key?(PARAM_REF) }&.fetch(PARAM_REF, {})
|
62
|
+
return unless (start_ref_loc = first_ref[LOCATION])
|
63
|
+
|
64
|
+
return unless (end_ref_loc = last_ref[LOCATION])
|
65
|
+
|
66
|
+
started = start_ref_loc + 1
|
67
|
+
ended = end_ref_loc + last_ref.fetch(NUMBER, '').to_s.size
|
68
|
+
[started, ended - started]
|
69
|
+
end
|
70
|
+
|
71
|
+
def wrap_array(object)
|
72
|
+
if object.respond_to?(:to_ary)
|
73
|
+
object.to_ary || [object]
|
74
|
+
else
|
75
|
+
[object]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgExecArrayParams
|
4
|
+
module Rewriters
|
5
|
+
class Node
|
6
|
+
attr_reader :value, :ref_idx
|
7
|
+
|
8
|
+
def initialize(value, ref_idx)
|
9
|
+
@value = value
|
10
|
+
@ref_idx = ref_idx
|
11
|
+
end
|
12
|
+
|
13
|
+
def process
|
14
|
+
rewrite! if should_rewrite?
|
15
|
+
end
|
16
|
+
|
17
|
+
# used in exception rendering
|
18
|
+
def to_s
|
19
|
+
return '<unknown node position>' unless (from, size = refs_at)
|
20
|
+
|
21
|
+
"#{'^'.rjust(from, ' ')}#{'-'.rjust(size, '-')}^"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# returns start and end index of value string repr inside query
|
27
|
+
# [from, size]
|
28
|
+
def refs_at; end
|
29
|
+
|
30
|
+
def should_rewrite?; end
|
31
|
+
|
32
|
+
def rewrite!; end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgExecArrayParams
|
4
|
+
module Rewriters
|
5
|
+
class ResTarget < Node
|
6
|
+
VAL = 'val'
|
7
|
+
|
8
|
+
def should_rewrite?
|
9
|
+
plain_selection?
|
10
|
+
end
|
11
|
+
|
12
|
+
def rewrite!
|
13
|
+
# puts({value_before: value}.inspect)
|
14
|
+
old_ref_idx = value[VAL][PARAM_REF][NUMBER] - 1 # one based
|
15
|
+
unless (new_ref_idx = ref_idx[old_ref_idx])
|
16
|
+
raise Error.new("No parameter for $#{old_ref_idx + 1}", nil, self)
|
17
|
+
end
|
18
|
+
|
19
|
+
if new_ref_idx.is_a?(Array)
|
20
|
+
elements = Range.new(*new_ref_idx).map do |param_ref_idx|
|
21
|
+
{ PARAM_REF => { NUMBER => param_ref_idx } }
|
22
|
+
end
|
23
|
+
value[VAL] = { 'A_ArrayExpr' => { 'elements' => elements } }
|
24
|
+
else
|
25
|
+
value[VAL][PARAM_REF][NUMBER] = new_ref_idx
|
26
|
+
end
|
27
|
+
# puts({value_after_: value, 'ref_idx' => ref_idx}.inspect)
|
28
|
+
end
|
29
|
+
|
30
|
+
# handle "select $1"
|
31
|
+
# {"val"=>{"ParamRef"=>{"number"=>1, "location"=>7}}, "location"=>7}
|
32
|
+
# AExpr handles "select $1 + 1"
|
33
|
+
def plain_selection?
|
34
|
+
value.key?(VAL) && value[VAL].is_a?(Hash) && value[VAL].key?(PARAM_REF)
|
35
|
+
end
|
36
|
+
|
37
|
+
def refs_at
|
38
|
+
[value[VAL][PARAM_REF][LOCATION] + 1, value[VAL][PARAM_REF][NUMBER].to_s.size]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgExecArrayParams
|
4
|
+
# Calculates inclusive bounds of each element in a flattened list
|
5
|
+
# Bounds are one-based (as sql ref indexes), single value for non-arrays
|
6
|
+
# [1, [2, 3], 4, [5, 6, 7]] => [1, [2, 3], 4, [5, 7]]
|
7
|
+
class SqlRefIndex
|
8
|
+
attr_reader :array
|
9
|
+
|
10
|
+
def initialize(array)
|
11
|
+
@array = array
|
12
|
+
@extra_items = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
sql_ref_index[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
def sql_ref_index
|
20
|
+
@sql_ref_index ||= array.map.with_index(1) do |item, idx|
|
21
|
+
if item.is_a?(Array)
|
22
|
+
add_extra_items = item.size
|
23
|
+
add_extra_items -= 1 if add_extra_items.positive?
|
24
|
+
[idx + @extra_items, idx + (@extra_items += add_extra_items)]
|
25
|
+
else
|
26
|
+
idx + @extra_items
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_exec_array_params
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vlad Bokov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg_query
|
@@ -69,8 +69,16 @@ files:
|
|
69
69
|
- Rakefile
|
70
70
|
- benchmark.rb
|
71
71
|
- lib/pg_exec_array_params.rb
|
72
|
+
- lib/pg_exec_array_params/column.rb
|
73
|
+
- lib/pg_exec_array_params/error.rb
|
72
74
|
- lib/pg_exec_array_params/query.rb
|
75
|
+
- lib/pg_exec_array_params/rewriters.rb
|
76
|
+
- lib/pg_exec_array_params/rewriters/a_expr.rb
|
77
|
+
- lib/pg_exec_array_params/rewriters/node.rb
|
78
|
+
- lib/pg_exec_array_params/rewriters/res_target.rb
|
79
|
+
- lib/pg_exec_array_params/sql_ref_index.rb
|
73
80
|
- lib/pg_exec_array_params/version.rb
|
81
|
+
- pg_exec_array_params-0.1.0.gem
|
74
82
|
- pg_exec_array_params.gemspec
|
75
83
|
homepage: https://github.com/lunatic-cat/pg_exec_array_params
|
76
84
|
licenses:
|
@@ -93,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
101
|
- !ruby/object:Gem::Version
|
94
102
|
version: '0'
|
95
103
|
requirements: []
|
96
|
-
rubygems_version: 3.
|
104
|
+
rubygems_version: 3.0.8
|
97
105
|
signing_key:
|
98
106
|
specification_version: 4
|
99
107
|
summary: PG::Connection#exec_params with arrays
|