1266: def record_query(record_class, command_template, *command_arguments)
1267: command = command_template.to_s.gsub(/\$([^0-9]|$)/) {
1268: if command_arguments.empty?
1269: raise ArgumentError, "Too few arguments supplied for SQL command."
1270: end
1271: command_argument = command_arguments.shift
1272: if command_argument.kind_of? Rational
1273: command_argument = command_argument.to_f
1274: elsif command_argument.kind_of? Time
1275: time_offset_hours = command_argument.utc_offset / 3600
1276: unless 3600 * time_offset_hours == command_argument.utc_offset
1277: raise "Time zone offset is not an integer amount of hours."
1278: end
1279: command_argument = "%s.%06i%+03i" % [command_argument.strftime("%Y-%m-%d %H:%M:%S"), command_argument.usec, time_offset_hours]
1280: end
1281: PGconn.quote(command_argument) << $1
1282:
1283:
1284:
1285:
1286:
1287:
1288: }
1289: raise ArgumentError, "Too many arguments supplied for SQL command." unless command_arguments.empty?
1290: if $flexirecord_debug_output
1291: $flexirecord_debug_output << "#{command}\n"
1292: end
1293: begin
1294: synchronize do
1295: if record_class
1296: backend_result = @backend_connection.async_exec(command)
1297: result = FlexiRecord::RecordArray.new(record_class)
1298: for row in 0...(backend_result.num_tuples)
1299: record_data = {}
1300: for col in 0...(backend_result.num_fields)
1301: value_string = backend_result.getvalue(row, col)
1302: record_data[backend_result.fieldname(col)] = if value_string.nil?
1303: nil
1304: else
1305: if @data_types
1306: data_type = @data_types[backend_result.type(col)]
1307: if data_type == "bool"
1308: value_string[0, 1] == 't'
1309: elsif ["int2", "int4", "int8"].include? data_type
1310: value_string.to_i
1311: elsif ["text", "varchar"].include? data_type
1312: value_string
1313: elsif data_type == "numeric"
1314: unless value_string =~ /^([0-9]*)(\.([0-9]+)?)?$/
1315: raise "Unexpected format for numeric data from database."
1316: end
1317: if $3
1318: $1.to_i + Rational($3.to_i, 10**($3.length))
1319: else
1320: $1.to_i
1321: end
1322: elsif data_type == "date"
1323: unless value_string =~ /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/
1324: raise "Unexpected format for date from database."
1325: end
1326: Time.local($1.to_i, $2.to_i, $3.to_i)
1327: elsif data_type == "timestamp"
1328: unless value_string =~ /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]*))?$/
1329: raise "Unexpected format for timestamp without time zone from database."
1330: end
1331: Time.local($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, $8.to_i)
1332: elsif data_type == "timestamptz"
1333: unless value_string =~ /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})(\.([0-9]*))?([+-][0-9]{2})$/
1334: aise "Unexpected format for timestamp with time zone from database."
1335: end
1336: (Time.utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, $8.to_i) - (3600 * $9.to_i)).localtime
1337: else
1338: value_string
1339: end
1340: else
1341: value_string
1342: end
1343: end
1344: end
1345: result << record_class.new(record_data, true)
1346: end
1347: return result
1348: else
1349: @backend_connection.async_exec(command)
1350: return nil
1351: end
1352: end
1353: rescue PGError
1354: raise FlexiRecord::DatabaseError, $!.message
1355: end
1356: end