Class: Qreport::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/qreport/connection.rb,
lib/qreport/connection/query.rb

Defined Under Namespace

Modules: TypeName Classes: Query, SQL

Constant Summary collapse

NULL =
'NULL'.freeze
QUOTE =
"'".freeze
T_ =
"'t'::boolean".freeze
F_ =
"'f'::boolean".freeze
T =
't'.freeze
S_TIMESTAMP =
"::timestamp".freeze
IDENTITY =
lambda { | val, type | val }
@@require_pg =
true

Class Attribute Summary collapse

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = nil) ⇒ Connection

Returns a new instance of Connection.



18
19
20
21
22
23
24
# File 'lib/qreport/connection.rb', line 18

def initialize args = nil
  @arguments = args
  initialize_copy nil
  if conn = @arguments && @arguments.delete(:conn)
    self.conn = conn
  end
end

Class Attribute Details

.currentObject

Returns the value of attribute current.



15
16
17
# File 'lib/qreport/connection.rb', line 15

def current
  @current
end

Instance Attribute Details

#argumentsObject

Returns the value of attribute arguments.



8
9
10
# File 'lib/qreport/connection.rb', line 8

def arguments
  @arguments
end

#connObject

Returns the PG connection object. Create a new connection from #arguments. New connection will be closed by #close.



50
51
52
# File 'lib/qreport/connection.rb', line 50

def conn
  @conn
end

#conn_ownedObject

Returns the value of attribute conn_owned.



11
12
13
# File 'lib/qreport/connection.rb', line 11

def conn_owned
  @conn_owned
end

#envObject

Returns the value of attribute env.



8
9
10
# File 'lib/qreport/connection.rb', line 8

def env
  @env
end

#schemanameObject

Returns the value of attribute schemaname.



10
11
12
# File 'lib/qreport/connection.rb', line 10

def schemaname
  @schemaname
end

#unescape_value_funcsObject

Returns the value of attribute unescape_value_funcs.



12
13
14
# File 'lib/qreport/connection.rb', line 12

def unescape_value_funcs
  @unescape_value_funcs
end

#verboseObject

Returns the value of attribute verbose.



9
10
11
# File 'lib/qreport/connection.rb', line 9

def verbose
  @verbose
end

#verbose_resultObject

Returns the value of attribute verbose_result.



9
10
11
# File 'lib/qreport/connection.rb', line 9

def verbose_result
  @verbose_result
end

#verbose_streamObject

Returns the value of attribute verbose_stream.



9
10
11
# File 'lib/qreport/connection.rb', line 9

def verbose_stream
  @verbose_stream
end

Instance Method Details

#_closeObject



83
84
85
86
87
88
89
90
91
92
# File 'lib/qreport/connection.rb', line 83

def _close
  if @conn
    conn = @conn
    @conn = nil
    conn.close if @conn_owned
  end
ensure
  @conn_owned = false
  @transaction_nesting = 0
end

#_transaction_abortObject



144
145
146
# File 'lib/qreport/connection.rb', line 144

def _transaction_abort
  run "ABORT"; self
end

#_transaction_beginObject



136
137
138
# File 'lib/qreport/connection.rb', line 136

def _transaction_begin
  run "BEGIN"; self
end

#_transaction_commitObject



140
141
142
# File 'lib/qreport/connection.rb', line 140

def _transaction_commit
  run "COMMIT"; self
end

#_type_name(args) ⇒ Object



296
297
298
299
300
301
302
303
304
# File 'lib/qreport/connection.rb', line 296

def _type_name args
  x = conn.exec("SELECT pg_catalog.format_type($1,$2)", args).
    getvalue(0, 0).to_s.dup
  # x = ":#{args * ','}" if x.empty? or x == "unknown"
  x.extend(TypeName)
  x.pg_ftype, x.pg_fmod = args
  x.freeze
  x
end

#closeObject



76
77
78
79
80
81
# File 'lib/qreport/connection.rb', line 76

def close
  raise Error, "close during transaction" if in_transaction?
  _close
ensure
  @invalid = false
end

#dump_result!(result, stream = nil) ⇒ Object



280
281
282
283
# File 'lib/qreport/connection.rb', line 280

def dump_result! result, stream = nil
  PP.pp(result, stream || verbose_stream) if result
  result
end

#escape_identifier(name) ⇒ Object



187
188
189
# File 'lib/qreport/connection.rb', line 187

def escape_identifier name
  conn.escape_identifier name.to_s
end

#escape_value(val, example_value = nil) ⇒ Object



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/qreport/connection.rb', line 191

def escape_value val, example_value = nil
  example_val ||= val
  case val
  when SQL
    val.to_s
  when nil
    NULL
  when true
    T_
  when false
    F_
  when Rational
    val.to_f
  when Numeric
    val
  when String, Symbol
    "'" << conn.escape_string(val.to_s) << QUOTE
  when Time
    escape_value(val.iso8601(6)) << S_TIMESTAMP
  when Range
    "BETWEEN #{escape_value(val.first)} AND #{escape_value(val.last)}"
  when Hash
    escape_value(val.to_json)
  when Array
    case
    when true
      # PUNT!!!
      escape_value(val.to_json)
      # DOES NOT HANDLE EMPTY ARRAY!!!
    when example_val.all?{|x| Numeric === x || x.nil?} && ! example_val.empty?
      "ARRAY[#{val.map{|x| escape_value(x, example_val[0])} * ','}]"
    else
      # PUNT!!!
      escape_value(val.to_json)
    end
  else
    raise TypeError, "cannot escape_value on #{val.class.name}"
  end.to_s
end

#fdObject



72
73
74
# File 'lib/qreport/connection.rb', line 72

def fd
  @conn && @conn.socket
end

#in_transaction?Boolean

Returns:

  • (Boolean)


95
# File 'lib/qreport/connection.rb', line 95

def in_transaction?; @transaction_nesting > 0; end

#initialize_copy(src) ⇒ Object



26
27
28
29
30
31
# File 'lib/qreport/connection.rb', line 26

def initialize_copy src
  @conn = @conn_owned = nil
  @abort_transaction = @invalid = nil
  @unescape_value_funcs_cache = nil
  @transaction_nesting = 0
end

#run(sql, options = nil) ⇒ Object

options:

:arguments => { :key => value, ... }
:limit => size
:limit => [ size, offset ]


164
165
166
167
168
169
170
171
172
173
174
# File 'lib/qreport/connection.rb', line 164

def run sql, options = nil
  options ||= { }
  conn = options[:connection] || self.conn
  result = Query.new
  result.sql = sql
  result.options = options
  result.conn = self
  result.run!
  dump_result! result if @verbose_result || options[:verbose_result]
  result
end

#run_query!(sql, query, options = nil) ⇒ Object



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/qreport/connection.rb', line 320

def run_query! sql, query, options = nil
  error = nil
  options ||= EMPTY_Hash
  # $stderr.puts "  run_query! options = #{options.inspect}"
  result = nil
  begin
    result = conn.async_exec(sql)
  rescue ::PG::Error => exc
    error = exc
  rescue ::StandardError => exc
    @invalid = true
    error = exc
  end
  result
ensure
  if error
    # $stderr.puts "  ERROR: #{exc.inspect}\n  #{exc.backtrace * "\n  "}"
    query.error = error.inspect
    raise error unless options[:capture_error]
  end
end

#safe_sql(x) ⇒ Object



183
184
185
# File 'lib/qreport/connection.rb', line 183

def safe_sql x
  SQL.new(x)
end

#table_exists?(name, schemaname = nil) ⇒ Boolean

Returns:

  • (Boolean)


148
149
150
151
152
153
154
155
156
157
158
# File 'lib/qreport/connection.rb', line 148

def table_exists? name, schemaname = nil
  schema_name = name.split('.', 2)
  schema = schema_name.shift if schema_name.size > 1
  name = schema_name.first
  schema ||= schemaname || self.schemaname || 'public'
  result =
  run "SELECT EXISTS(SELECT * FROM pg_catalog.pg_tables WHERE tablename = :tablename AND schemaname = :schemaname) as \"exists\"",
  :arguments => { :tablename => name, :schemaname => schema }
  # result.rows; pp result
  result.rows[0]["exists"]
end

#transactionObject

Raises:



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/qreport/connection.rb', line 97

def transaction
  raise Error, "no block" unless block_given?
  abort = false
  begin
    transaction_begin
    yield
  rescue ::StandardError => exc
    abort = @abort_transaction = exc
    raise exc
  ensure
    transaction_end abort
  end
end

#transaction_beginObject



111
112
113
114
115
116
117
# File 'lib/qreport/connection.rb', line 111

def transaction_begin
  if @transaction_nesting == 0
    _transaction_begin
  end
  @transaction_nesting += 1
  self
end

#transaction_end(abort = nil) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/qreport/connection.rb', line 119

def transaction_end abort = nil
  if (@transaction_nesting -= 1) == 0
    begin
      if abort
        _transaction_abort
      else
        _transaction_commit
      end
    ensure
      if @invalid
        close
      end
    end
  end
  self
end

#type_name(*args) ⇒ Object

Returns a frozen String representing a column type. The String also responds to #pg_ftype and #pg_fmod.



287
288
289
290
# File 'lib/qreport/connection.rb', line 287

def type_name *args
  (@type_names ||= { })[args] ||=
    _type_name(args)
end

#unescape_value(val, type) ⇒ Object



237
238
239
240
241
242
243
244
245
246
# File 'lib/qreport/connection.rb', line 237

def unescape_value val, type
  case val
  when nil
  when String
    return nil if val == NULL
    func = (@unescape_value_funcs_cache ||= { })[type] ||= unescape_value_func(type)
    val = func.call(val, type)
  end
  val
end

#unescape_value_func(type) ⇒ Object



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/qreport/connection.rb', line 248

def unescape_value_func type
  if @unescape_value_funcs and func = @unescape_value_funcs[type]
    return func
  end
  case type
  when /\[\]\Z/
    et = $`
    el = unescape_value_func(et)
    lambda do | val, type |
#          PP.pp([ val, type, et ])
      val.gsub(/\A\{|\}\Z/, '').
        split(',').
        map{|x| x == 'NULL' ? nil : el.call(x, et)}
    end
  when /^bool/
    lambda { | val, type | val == T }
  when /^(int|smallint|bigint|oid|tid|xid|cid)/
    lambda { | val, type | val.to_i }
  when /^(float|real|double|numeric)/
    lambda { | val, type | val.to_f }
  when /^timestamp/
    lambda { | val, type | Time.parse(val) }
  else
    IDENTITY
  end
end

#with_limit(sql, limit = nil) ⇒ Object



306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/qreport/connection.rb', line 306

def with_limit sql, limit = nil
  sql = sql.dup
  case limit
  when Integer
    limit = "LIMIT #{limit}"
  when Array
    limit = "OFFSET #{limit[1].to_i}\nLIMIT #{limit[0].to_i}"
  end
  unless sql.sub!(/:LIMIT\b|\bLIMIT\s+\S+\s*|\Z/im, "\n#{limit}")
    sql << "\n" << limit
  end
  sql
end