nitro 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/ChangeLog +186 -0
  2. data/README +40 -11
  3. data/RELEASES +10 -1
  4. data/Rakefile +5 -4
  5. data/bin/cluster.rb +3 -3
  6. data/{etc/new-project.rb → bin/new_app.rb} +1 -1
  7. data/examples/og/README +4 -0
  8. data/examples/og/run.rb +254 -0
  9. data/examples/simple/app.rb +3 -3
  10. data/examples/simple/conf/config.rb +10 -22
  11. data/examples/simple/conf/debug-config.rb +6 -32
  12. data/examples/simple/conf/live-config.rb +3 -23
  13. data/examples/simple/conf/requires.rb +5 -5
  14. data/examples/simple/env.rb +3 -4
  15. data/examples/simple/lib/articles/entities.rb +17 -15
  16. data/examples/simple/lib/articles/methods.rb +15 -15
  17. data/examples/simple/lib/articles/part.rb +7 -8
  18. data/examples/simple/root/comments.si +1 -1
  19. data/examples/simple/root/index.sx +1 -1
  20. data/examples/simple/root/view-article.sx +1 -2
  21. data/examples/tiny/app.rb +3 -3
  22. data/examples/tiny/conf/config.rb +4 -4
  23. data/examples/tiny/conf/requires.rb +3 -4
  24. data/lib/n/config.rb +50 -3
  25. data/lib/n/logger.rb +14 -2
  26. data/lib/n/og.rb +381 -0
  27. data/lib/n/og/backend.rb +252 -0
  28. data/lib/n/og/backends/mysql.rb +352 -0
  29. data/lib/n/og/backends/psql.rb +351 -0
  30. data/lib/n/og/connection.rb +253 -0
  31. data/lib/n/og/meta.rb +127 -0
  32. data/lib/n/properties.rb +6 -6
  33. data/lib/n/server.rb +4 -7
  34. data/lib/n/server/appserver.rb +58 -0
  35. data/lib/n/{app → server}/cluster.rb +3 -3
  36. data/lib/n/{app → server}/cookie.rb +3 -3
  37. data/lib/n/server/dispatcher.rb +55 -0
  38. data/lib/n/server/{filter.rb → filters.rb} +1 -1
  39. data/lib/n/{app → server}/filters/autologin.rb +5 -5
  40. data/lib/n/{app → server}/fragment.rb +3 -3
  41. data/lib/n/{app → server}/handlers.rb +4 -4
  42. data/lib/n/{app → server}/handlers/code-handler.rb +6 -6
  43. data/lib/n/{app → server}/handlers/page-handler.rb +9 -7
  44. data/lib/n/{app → server}/request.rb +8 -8
  45. data/lib/n/{app/request-part.rb → server/requestpart.rb} +4 -4
  46. data/lib/n/{app → server}/script.rb +5 -5
  47. data/lib/n/{app → server}/server.rb +1 -1
  48. data/lib/n/{app → server}/session.rb +5 -5
  49. data/lib/n/{app → server}/user.rb +1 -1
  50. data/lib/n/{app/webrick-servlet.rb → server/webrick.rb} +77 -20
  51. data/lib/n/shaders.rb +3 -2
  52. data/lib/n/std.rb +5 -32
  53. data/test/n/{app → server}/tc_cookie.rb +2 -2
  54. data/test/n/server/tc_filters.rb +38 -0
  55. data/test/n/{app → server}/tc_request.rb +6 -6
  56. data/test/n/{app → server}/tc_requestpart.rb +3 -3
  57. data/test/n/{app → server}/tc_session.rb +2 -2
  58. data/test/n/tc_og.rb +178 -0
  59. data/test/n/ui/tc_pager.rb +3 -3
  60. metadata +41 -65
  61. data/examples/ndb/README +0 -5
  62. data/examples/ndb/run.rb +0 -271
  63. data/lib/n/app/webrick.rb +0 -73
  64. data/lib/n/db.rb +0 -233
  65. data/lib/n/db/README +0 -232
  66. data/lib/n/db/connection.rb +0 -365
  67. data/lib/n/db/managed.rb +0 -233
  68. data/lib/n/db/mixins.rb +0 -279
  69. data/lib/n/db/mysql.rb +0 -345
  70. data/lib/n/db/psql.rb +0 -383
  71. data/lib/n/db/tools.rb +0 -106
  72. data/lib/n/db/utils.rb +0 -102
  73. data/lib/n/server/PLAYBACK.txt +0 -8
  74. data/lib/n/server/RESEARCH.txt +0 -13
  75. data/test/n/tc_db.rb +0 -223
  76. data/test/n/tc_db_mysql.rb +0 -241
data/lib/n/db/mysql.rb DELETED
@@ -1,345 +0,0 @@
1
- # = MySQL backend
2
- #
3
- # Implement the Db backend using the MySQL RDBMS.
4
- # Include the MySQL backend to the standard DbConnection
5
- # object to synthesize a MySQLConnection at runtime.
6
- #
7
- # EXPERIMENTAL: NOT WORKING YET
8
- #
9
- # code:
10
- # Elias Athanasopoulos <elathan@navel.gr>
11
- # George Moschovitis <gm@navel.gr>
12
- #
13
- # (c) 2004 Navel, all rights reserved.
14
- # $Id: mysql.rb 98 2004-10-22 07:36:20Z gmosx $
15
-
16
- require "mysql"
17
- require "time.rb"
18
- require "date.rb"
19
-
20
- module N;
21
-
22
- # = DbUtils
23
- #
24
- # Backend specific utils. Extend the base utils
25
- # collection.
26
- #
27
- module DbUtils
28
-
29
- # Escape an sql string
30
- #
31
- def self.escape(str)
32
- return nil unless str
33
- return Mysql.quote(str)
34
- end
35
-
36
- # Convert a ruby time to an sql timestamp.
37
- #
38
- def self.sql_timestamp(time = Time.now)
39
- return nil unless time
40
- return time.strftime("%Y-%m-%d %H:%M:%S")
41
- end
42
-
43
- # Output YYY-mm-dd
44
- #
45
- def self.sql_date(date)
46
- return nil unless date
47
- return "#{date.year}-#{date.month}-#{date.mday}"
48
- end
49
-
50
- # Parse sql datetime
51
- #
52
- # TODO: Optimize this
53
- #
54
- def self.parse_sql_timestamp(str)
55
- return Time.parse(str)
56
- end
57
-
58
- # Input YYYY-mm-dd
59
- #
60
- def self.parse_sql_date(str)
61
- return nil unless str
62
- return Date.strptime(str)
63
- end
64
-
65
- # Return an evaluator for reading the property
66
- # No need to optimize this, used only to precalculate code.
67
- #
68
- def self.read_prop(p, idx)
69
- case p.klass.to_s
70
- when Fixnum.name
71
- return "rows.getvalue(tuple, #{idx}).to_i()"
72
- when Float.name
73
- return "rows.getvalue(tuple, #{idx}).to_f()"
74
- when Time.name
75
- return "N::DbUtils.parse_sql_timestamp(rows.getvalue(tuple, #{idx}))"
76
- when Date.name
77
- return "N::DbUtils.parse_sql_date(rows.getvalue(tuple, #{idx}))"
78
- when TrueClass.name
79
- return "('true' == rows.getvalue(tuple, #{idx}))"
80
- else # String
81
- return "rows.getvalue(tuple, #{idx})"
82
- end
83
- end
84
- end
85
-
86
- # = MysqlBackend
87
- #
88
- # Implement the Db backend using the MySQL RDBMS.
89
- #
90
- module MysqlBackend
91
-
92
- # map between Ruby and SQL types
93
- #
94
- TYPEMAP = {
95
- Integer => "integer",
96
- Fixnum => "integer",
97
- Float => "float",
98
- String => "text",
99
- Time => "timestamp",
100
- Date => "date",
101
- TrueClass => "boolean",
102
- Array => "bytea",
103
- Hash => "bytea"
104
- }
105
-
106
- # Initialize a connection to the database
107
- #
108
- def initialize(config)
109
- @rdb = Mysql.connect(config[:address], config[:user], config[:password], config[:database])
110
- end
111
-
112
- # Close the connection to the database
113
- #
114
- def close()
115
- @rdb.close
116
- end
117
-
118
- # Create the sequence that generates the unified space id
119
- # oids. You *MUST* call this method on newly created
120
- # databases.
121
- #
122
- def create_schema()
123
- @rdb.query("CREATE SEQUENCE oids_seq")
124
- end
125
-
126
- # Drop the oid sequence
127
- #
128
- def drop_schema()
129
- @rdb.query("DROP SEQUENCE oids_seq")
130
- end
131
-
132
- # NOT IMPLEMENTED
133
- #
134
- def prepare_statement()
135
- @rdb.query("PREPARE")
136
- end
137
- alias_method :pstatement, :prepare_statement
138
-
139
- # NOT IMPLEMENTED
140
- #
141
- def execute_statement()
142
- self.select("EXECUTE")
143
- end
144
- alias_method :xstatement, :execute_statement
145
-
146
- # Create a table for an entity.
147
- #
148
- def create_table(klass)
149
- fields = []
150
- klass.__props.each { |p|
151
- field = "#{p.symbol}"
152
- if p.sql_type
153
- field << " #{p.sql_type}"
154
- else
155
- field << " #{TYPEMAP[p.klass]}"
156
- end
157
- field << " #{p.sql}" if p.sql
158
-
159
- field << " NOT NULL AUTO_INCREMENT" if p.symbol == :oid
160
- fields << field
161
- }
162
-
163
- sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
164
-
165
- # Create table constrains
166
-
167
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
168
- sql << ", #{constrains.join(', ')}"
169
- end
170
-
171
- sql << ");"
172
- safe_query(sql)
173
-
174
- $log.info "Created table #{klass::DBTABLE}! :: #{sql}"
175
-
176
- # Create indices
177
- if klass.__meta
178
- for data in klass.__meta[:sql_index]
179
- sql = ""
180
- idx = data[0]
181
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
182
- sql << " CREATE"
183
- sql << " UNIQUE" if data[1]
184
- sql << " INDEX #{klass::DBTABLE}_#{idxname}_idx #{data[3]} ON #{klass::DBTABLE} (#{idx});"
185
- safe_query(sql)
186
- end
187
- end
188
- end
189
-
190
- # Drop the entity table
191
- #
192
- def drop_table(klass)
193
- safe_query("DROP TABLE #{klass::DBTABLE}")
194
- end
195
-
196
- # Calculate the fields map for the given class
197
- #
198
- def calc_fields(rows, klass)
199
- # gmosx SOS, FIXME, INVESTIGATE: no need for second safe hash ????
200
- fields = $db.fields[klass] = {}
201
- for field in rows.fields
202
- fields[field] = rows.fieldnum(field)
203
- end
204
- N::Managed.eval_db_read_row(klass)
205
- end
206
-
207
- # Grab the join fields returned by an sql join query, and attach them
208
- # to the entity.
209
- #
210
- def get_join_fields(rows, tuple, entity, join_fields)
211
- entity.join_fields = {}
212
- for f in join_fields
213
- entity.join_fields[f] = rows.getvalue(tuple, $db.fields[entity.class][f.to_s])
214
- end
215
- end
216
-
217
- # If the connection is in deserialize mode, deserialize one row.
218
- #
219
- def deserialize_one(rows, klass, join_fields = nil)
220
- return nil unless rows
221
-
222
- calc_fields(rows, klass) unless $db.fields[klass]
223
-
224
- if @deserialize
225
- # gmosx: Enities should have no params constructor SOS.
226
- #
227
- entity = klass.new()
228
- entity.__db_read_row(rows, 0)
229
-
230
- get_join_fields(rows, 0, entity, join_fields) if join_fields
231
-
232
- rows.clear()
233
- return entity
234
- end
235
-
236
- return rows[0]
237
- end
238
-
239
- # If the connection is in deserialize mode, deserialize all rows.
240
- #
241
- def deserialize_all(rows, klass, join_fields = nil)
242
- return nil unless rows
243
-
244
- calc_fields(rows, klass) unless $db.fields[klass]
245
-
246
- if @deserialize
247
- entities = []
248
-
249
- for tuple in (0...rows.num_tuples)
250
- entity = klass.new()
251
- entity.__db_read_row(rows, tuple)
252
-
253
- get_join_fields(rows, tuple, entity, join_fields) if join_fields
254
-
255
- entities << entity
256
- end
257
-
258
- rows.clear()
259
- return entities
260
- end
261
-
262
- return rows
263
- end
264
-
265
- #
266
- # Execute an sql query. If the entity table is missing, create
267
- # it and retry.
268
- #
269
- def retry_query(sql, klass = nil)
270
- $log.debug sql if $DBG
271
- retries = 0
272
- begin
273
- rows = @rdb.query(sql)
274
- if rows && (rows.num_tuples > 0)
275
- return rows
276
- else
277
- return nil
278
- end
279
- rescue => ex
280
- if ex.errno == 1146 # table does not exist
281
- $log.info "retry_query: #{ex}"
282
- # table does not exist, create it!
283
- create_table(klass)
284
- # gmosx: only allow ONE retry to avoid loops here!
285
- retries += 1
286
- retry if retries <= 1
287
- else
288
- $log.error "DB Error: #{ex} (#{ex.errno}), [#{sql}]"
289
- $log.error "#{caller[0]} : #{caller[1]} : #{caller[2]}"
290
- return nil
291
- end
292
- end
293
- end
294
-
295
- # Execute an sql query and catch the errors.
296
- #
297
- def safe_query(sql)
298
- $log.debug sql if $DBG
299
- begin
300
- rows = @rdb.query(sql)
301
- if rows #&& (rows.num_tuples > 0)
302
- return rows
303
- else
304
- return nil
305
- end
306
- rescue => ex
307
- $log.error "DB Error (safe_query): #{ex} (#{ex.errno}), [#{sql}]"
308
- $log.error "#{caller[0]} : #{caller[1]} : #{caller[2]}"
309
- return nil
310
- end
311
- end
312
-
313
- # Get the next oid in the sequence for this klass
314
- #
315
- def next_oid(klass)
316
- retries = 0
317
- begin
318
- res = @rdb.insert_id()
319
- res ? oid = res + 1 : oid = 1
320
- return oid
321
- rescue => ex
322
- if ex.errno == 1146 # table does not exist
323
- $log.info "next_oid: #{ex}"
324
- # table does not exist, create it!
325
- create_table(klass)
326
- # gmosx: only allow ONE retry to avoid loops here!
327
- retries += 1
328
- retry if retries <= 1
329
- else
330
- $log.error "DB Error (next_oid): #{ex} (#{ex.errno})"
331
- $log.error "#{caller[0]} : #{caller[1]} : #{caller[2]}"
332
- return nil
333
- end
334
- end
335
- end
336
-
337
- end
338
-
339
- # Mix into the DbConnection class
340
- #
341
- N::DbConnection.module_eval %{
342
- include N::MysqlBackend
343
- }
344
-
345
- end # namespace
data/lib/n/db/psql.rb DELETED
@@ -1,383 +0,0 @@
1
- # code:
2
- # * George Moschovitis <gm@navel.gr>
3
- #
4
- # (c) 2004 Navel, all rights reserved.
5
- # $Id$
6
-
7
- require "base64"
8
- require "postgres"
9
- require "time.rb"
10
- require "date.rb"
11
-
12
- module N;
13
-
14
- # = DbUtils
15
- #
16
- # Backend specific utils
17
- #
18
- module DbUtils
19
-
20
- # Escape an sql string
21
- #
22
- def self.escape(str)
23
- return nil unless str
24
- return PGconn.escape(str)
25
- end
26
-
27
- # Escape bytes
28
- #
29
- def self.escape_bytes(bytes)
30
- return nil unless bytes
31
- return PGconn.escape_bytea(bytes)
32
- end
33
-
34
- # Unescape bytes
35
- # FIXME: optimize this! Even better integrate this in the
36
- # libpq library, or find out why the default method doesnt
37
- # work.
38
- #
39
- def self.unescape_bytes(bytes)
40
- return bytes.gsub(/(\\\d+)/) { |m| m.gsub(/\\/, "").oct.chr }
41
- end
42
-
43
- # Convert a ruby time to an sql timestamp.
44
- #
45
- def self.sql_timestamp(time = Time.now)
46
- return nil unless time
47
- return time.strftime("%Y-%m-%d %H:%M:%S")
48
- end
49
-
50
- # Output YYY-mm-dd
51
- #
52
- def self.sql_date(date)
53
- return nil unless date
54
- return "#{date.year}-#{date.month}-#{date.mday}"
55
- end
56
-
57
- # Parse sql datetime
58
- #
59
- # TODO: Optimize this
60
- #
61
- def self.parse_sql_timestamp(str)
62
- return Time.parse(str)
63
- end
64
-
65
- # Input YYYY-mm-dd
66
- #
67
- def self.parse_sql_date(str)
68
- return nil unless str
69
- return Date.strptime(str)
70
- end
71
-
72
- # Return an evaluator for reading the property
73
- # No need to optimize this, used only to precalculate code.
74
- #--
75
- # gmosx: base64 encoding is used because the unencode_bytea
76
- # method of postgres is not implemented
77
- #++
78
- #
79
- def self.read_prop(p, idx)
80
- case p.klass.to_s
81
- when Fixnum.name
82
- return "rows.getvalue(tuple, #{idx}).to_i()"
83
- when Float.name
84
- return "rows.getvalue(tuple, #{idx}).to_f()"
85
- when Time.name
86
- return "N::DbUtils.parse_sql_timestamp(rows.getvalue(tuple, #{idx}))"
87
- when Date.name
88
- return "N::DbUtils.parse_sql_date(rows.getvalue(tuple, #{idx}))"
89
- when TrueClass.name
90
- return "('true' == rows.getvalue(tuple, #{idx}))"
91
- when Object.name
92
- return "Marshal.load(Base64.decode64(rows.getvalue(tuple, #{idx}).gsub(/\\\\012/, '\n')))"
93
- when Array.name
94
- return "Marshal.load(Base64.decode64(rows.getvalue(tuple, #{idx}).gsub(/\\\\012/, '\n')))"
95
- # return "Marshal.load(N::DbUtils.unescape_bytes(rows.getvalue(tuple, #{idx})))"
96
- when Hash.name
97
- return "Marshal.load(Base64.decode64(rows.getvalue(tuple, #{idx}).gsub(/\\\\012/, '\n')))"
98
- else # String
99
- return "rows.getvalue(tuple, #{idx})"
100
- end
101
- end
102
- end
103
-
104
- # = PsqlBackend
105
- #
106
- # Implement the Db backend using the PostgreSQL RDBMS.
107
- #
108
- # Include the Psql backend to the standard DbConnection
109
- # object to synthesize a PsqlConnection at runtime.
110
- #
111
- module PsqlBackend
112
-
113
- # map between Ruby and SQL types
114
- #
115
- TYPEMAP = {
116
- Integer => "integer",
117
- Fixnum => "integer",
118
- Float => "float",
119
- String => "text",
120
- Time => "timestamp",
121
- Date => "date",
122
- TrueClass => "boolean",
123
- Object => "bytea",
124
- Array => "bytea",
125
- Hash => "bytea"
126
- }
127
-
128
- # Initialize a connection to the database
129
- #
130
- def initialize(config)
131
- @rdb = PGconn.connect(nil, nil, nil, nil, config[:database],
132
- config[:user], config[:password])
133
- end
134
-
135
- # Close the connection to the database
136
- #
137
- def close()
138
- @rdb.close
139
- end
140
-
141
- # Create the sequence that generates the unified space id
142
- # oids. You *MUST* call this method on newly created
143
- # databases.
144
- #
145
- def create_schema()
146
- safe_query("CREATE SEQUENCE oids_seq")
147
- end
148
-
149
- # Drop the oid sequence
150
- #
151
- def drop_schema()
152
- safe_query("DROP SEQUENCE oids_seq")
153
- end
154
-
155
- # NOT IMPLEMENTED
156
- #
157
- def prepare_statement()
158
- @rdb.query("PREPARE")
159
- end
160
- alias_method :pstatement, :prepare_statement
161
-
162
- # NOT IMPLEMENTED
163
- #
164
- def execute_statement()
165
- self.select("EXECUTE")
166
- end
167
- alias_method :xstatement, :execute_statement
168
-
169
- # Create a table for an entity.
170
- #
171
- def create_table(klass)
172
- fields = []
173
- klass.__props.each { |p|
174
- field = "#{p.symbol}"
175
- if p.sql
176
- field << " #{p.sql}"
177
- else
178
- field << " #{TYPEMAP[p.klass]}"
179
- end
180
-
181
- fields << field
182
- }
183
-
184
- sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
185
-
186
- # Create table constrains
187
-
188
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
189
- sql << ", #{constrains.join(', ')}"
190
- end
191
-
192
- sql << ") WITHOUT OIDS;"
193
-
194
- # Create indices
195
-
196
- if klass.__meta
197
- for data in klass.__meta[:sql_index]
198
- idx, pre_sql, post_sql = *data
199
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
200
- sql << " CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx});"
201
- end
202
- end
203
-
204
- safe_query(sql)
205
- $log.info "Created table #{klass::DBTABLE}!"
206
-
207
- # create the sequence for this table. Even if the table
208
- # uses the oids_seq, attempt to create it. This makes
209
- # the system more fault tolerant.
210
- safe_query("CREATE SEQUENCE #{klass::DBSEQ}")
211
- $log.info "Created sequence #{klass::DBSEQ}!"
212
- end
213
-
214
- # Drop the entity table
215
- #
216
- def drop_table(klass)
217
- safe_query("DROP TABLE #{klass::DBTABLE}")
218
- if klass.include?(N::Sequenced)
219
- safe_query("DROP SEQUENCE #{klass::DBSEQ};")
220
- end
221
- end
222
-
223
- # Calculate the fields map for the given class
224
- #
225
- def calc_fields(rows, klass)
226
- # gmosx SOS, FIXME, INVESTIGATE: no need for second safe hash ????
227
- fields = $db.fields[klass] = {}
228
- for field in rows.fields
229
- fields[field] = rows.fieldnum(field)
230
- end
231
- N::Managed.eval_db_read_row(klass)
232
- end
233
-
234
- # Grab the join fields returned by an sql join query, and attach them
235
- # to the entity.
236
- #
237
- def get_join_fields(rows, tuple, entity, join_fields)
238
- entity.join_fields = {}
239
- for f in join_fields
240
- entity.join_fields[f] = rows.getvalue(tuple, $db.fields[entity.class][f.to_s])
241
- end
242
- end
243
-
244
- # If the connection is in deserialize mode, deserialize one row.
245
- #
246
- def deserialize_one(rows, klass, join_fields = nil)
247
- return nil unless rows
248
-
249
- calc_fields(rows, klass) unless $db.fields[klass]
250
-
251
- if @deserialize
252
- # gmosx: Enities should have no params constructor SOS.
253
- #
254
- entity = klass.new()
255
- entity.__db_read_row(rows, 0)
256
-
257
- get_join_fields(rows, 0, entity, join_fields) if join_fields
258
-
259
- rows.clear()
260
- return entity
261
- end
262
-
263
- return rows[0]
264
- end
265
-
266
- # If the connection is in deserialize mode, deserialize all rows.
267
- #
268
- def deserialize_all(rows, klass, join_fields = nil)
269
- return nil unless rows
270
-
271
- calc_fields(rows, klass) unless $db.fields[klass]
272
-
273
- if @deserialize
274
- entities = []
275
-
276
- for tuple in (0...rows.num_tuples)
277
- entity = klass.new()
278
- entity.__db_read_row(rows, tuple)
279
-
280
- get_join_fields(rows, tuple, entity, join_fields) if join_fields
281
-
282
- entities << entity
283
- end
284
-
285
- rows.clear()
286
- return entities
287
- end
288
-
289
- return rows
290
- end
291
-
292
- #
293
- # Execute an sql query. If the entity table is missing, create
294
- # it and retry.
295
- #
296
- # exec() is used instead of query because it is faster and we also
297
- # need fields()
298
- #
299
- # FIXME: is the result cleared?
300
- #
301
- def retry_query(sql, klass = nil)
302
- $log.debug sql if $DBG
303
- retries = 0
304
- begin
305
- rows = @rdb.exec(sql)
306
- if rows && (rows.num_tuples > 0)
307
- return rows
308
- else
309
- return nil
310
- end
311
- rescue => ex
312
- # Any idea how to better test this?
313
- if ex.to_s =~ /relation .* not exist/
314
- $log.info "RETRY_QUERY"
315
- # table does not exist, create it!
316
- create_table(klass)
317
- # gmosx: only allow ONE retry to avoid loops here!
318
- retries += 1
319
- retry if retries <= 1
320
- else
321
- $log.error "RETRY_QUERY: surpressing db error: #{ex}, [#{sql}]"
322
- # $log.debug "#{caller[0]} : #{caller[1]} : #{caller[2]}"
323
- return nil
324
- end
325
- end
326
- end
327
-
328
- # Execute an sql query and catch the errors.
329
- #
330
- # exec() is used instead of query because it is faster and we also
331
- # need fields()
332
- #
333
- def safe_query(sql)
334
- $log.debug sql if $DBG
335
- begin
336
- rows = @rdb.exec(sql)
337
- if rows && (rows.num_tuples > 0)
338
- return rows
339
- else
340
- return nil
341
- end
342
- rescue => ex
343
- $log.error "SAFE_QUERY: surpressing db error #{ex}, [#{sql}]"
344
- # $log.debug "#{caller[0]} : #{caller[1]} : #{caller[2]}"
345
- return nil
346
- end
347
- end
348
-
349
- # Get the next oid in the sequence for this klass
350
- #
351
- def next_oid(klass)
352
- retries = 0
353
- begin
354
- res = @rdb.exec("SELECT nextval('#{klass::DBSEQ}')")
355
- oid = res.getvalue(0, 0).to_i()
356
- res.clear()
357
- return oid
358
- rescue => ex
359
- # Any idea how to better test this?
360
- if ex.to_s =~ /relation .* not exist/
361
- $log.info "next_oid: #{ex}"
362
- # table does not exist, create it!
363
- create_table(klass)
364
- # gmosx: only allow ONE retry to avoid loops here!
365
- retries += 1
366
- retry if retries <= 1
367
- else
368
- $log.error "DB Error: #{ex}, #next_oid"
369
- $log.debug "#{caller[0]} : #{caller[1]} : #{caller[2]}"
370
- return nil
371
- end
372
- end
373
- end
374
-
375
- end
376
-
377
- # Mix into the DbConnection class
378
- #
379
- N::DbConnection.module_eval %{
380
- include N::PsqlBackend
381
- }
382
-
383
- end # namespace