Class: Qreport::Connection

Inherits:
Object
  • Object
show all
Defined in:
lib/qreport/connection.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
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.



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

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.



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

def current
  @current
end

Instance Attribute Details

#argumentsObject

Returns the value of attribute arguments.



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

def arguments
  @arguments
end

#connObject

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



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

def conn
  @conn
end

#conn_ownedObject

Returns the value of attribute conn_owned.



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

def conn_owned
  @conn_owned
end

#envObject

Returns the value of attribute env.



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

def env
  @env
end

#schemanameObject

Returns the value of attribute schemaname.



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

def schemaname
  @schemaname
end

#unescape_value_funcsObject

Returns the value of attribute unescape_value_funcs.



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

def unescape_value_funcs
  @unescape_value_funcs
end

#verboseObject

Returns the value of attribute verbose.



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

def verbose
  @verbose
end

#verbose_resultObject

Returns the value of attribute verbose_result.



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

def verbose_result
  @verbose_result
end

#verbose_streamObject

Returns the value of attribute verbose_stream.



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

def verbose_stream
  @verbose_stream
end

Instance Method Details

#_transaction_abortObject



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

def _transaction_abort
  run "ABORT"; self
end

#_transaction_beginObject



129
130
131
# File 'lib/qreport/connection.rb', line 129

def _transaction_begin
  run "BEGIN"; self
end

#_transaction_commitObject



133
134
135
# File 'lib/qreport/connection.rb', line 133

def _transaction_commit
  run "COMMIT"; self
end

#_type_name(args) ⇒ Object



264
265
266
267
268
269
270
271
272
# File 'lib/qreport/connection.rb', line 264

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



75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/qreport/connection.rb', line 75

def close
  raise Error, "close during transaction" if in_transaction?
  if @conn
    conn = @conn
    @conn = nil
    conn.close if @conn_owned
  end
ensure
  @invalid = false
  @transaction_nesting = 0
  @conn_owned = false
end

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



248
249
250
251
# File 'lib/qreport/connection.rb', line 248

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

#escape_identifier(name) ⇒ Object



180
181
182
# File 'lib/qreport/connection.rb', line 180

def escape_identifier name
  conn.escape_identifier name.to_s
end

#escape_value(val) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/qreport/connection.rb', line 184

def escape_value val
  case val
  when SQL
    val.to_s
  when nil
    NULL
  when true
    T_
  when false
    F_
  when Integer, Float
    val
  when String, Symbol
    "'" << conn.escape_string(val.to_s) << QUOTE
  when Time
    escape_value(val.iso8601(6)) << "::timestamp"
  when Range
    "BETWEEN #{escape_value(val.first)} AND #{escape_value(val.last)}"
  when Hash, Array
    escape_value(val.to_json)
  else
    raise TypeError, "cannot escape_value on #{val.class.name}"
  end.to_s
end

#fdObject



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

def fd
  @conn && @conn.socket
end

#in_transaction?Boolean

Returns:

  • (Boolean)


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

def in_transaction?; @transaction_nesting > 0; end

#initialize_copy(src) ⇒ Object



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

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 ]


157
158
159
160
161
162
163
164
165
166
167
# File 'lib/qreport/connection.rb', line 157

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



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/qreport/connection.rb', line 288

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

#safe_sql(x) ⇒ Object



176
177
178
# File 'lib/qreport/connection.rb', line 176

def safe_sql x
  SQL.new(x)
end

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

Returns:

  • (Boolean)


141
142
143
144
145
146
147
148
149
150
151
# File 'lib/qreport/connection.rb', line 141

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:



90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/qreport/connection.rb', line 90

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



104
105
106
107
108
109
110
# File 'lib/qreport/connection.rb', line 104

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

#transaction_end(abort = nil) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/qreport/connection.rb', line 112

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.



255
256
257
258
# File 'lib/qreport/connection.rb', line 255

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

#unescape_value(val, type) ⇒ Object



214
215
216
217
218
219
220
221
222
223
# File 'lib/qreport/connection.rb', line 214

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



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/qreport/connection.rb', line 225

def unescape_value_func type
  if @unescape_value_funcs and func = @unescape_value_funcs[type]
    return func
  end
  case type
  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



274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/qreport/connection.rb', line 274

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