sqlpostgres 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.md +23 -0
- data/README.rdoc +59 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/doc/BUGS +2 -0
- data/doc/examples/README +6 -0
- data/doc/examples/connection.rb +16 -0
- data/doc/examples/connection_auto.rb +22 -0
- data/doc/examples/connection_ctor.rb +18 -0
- data/doc/examples/connection_default.rb +15 -0
- data/doc/examples/connection_exec.rb +18 -0
- data/doc/examples/connection_manual.rb +12 -0
- data/doc/examples/connection_wrapped_new.rb +13 -0
- data/doc/examples/connection_wrapped_open.rb +13 -0
- data/doc/examples/cursor.rb +38 -0
- data/doc/examples/include_module.rb +9 -0
- data/doc/examples/include_module2.rb +12 -0
- data/doc/examples/insert.rb +30 -0
- data/doc/examples/insert2.rb +36 -0
- data/doc/examples/insert_bytea.rb +16 -0
- data/doc/examples/insert_bytea_array.rb +17 -0
- data/doc/examples/insert_default_values.rb +16 -0
- data/doc/examples/insert_insert.rb +16 -0
- data/doc/examples/insert_insert_default.rb +16 -0
- data/doc/examples/insert_insert_select.rb +20 -0
- data/doc/examples/insert_select.rb +20 -0
- data/doc/examples/interval.rb +17 -0
- data/doc/examples/savepoint.rb +38 -0
- data/doc/examples/select.rb +33 -0
- data/doc/examples/select2.rb +36 -0
- data/doc/examples/select_cross_join.rb +18 -0
- data/doc/examples/select_distinct.rb +18 -0
- data/doc/examples/select_distinct_on +19 -0
- data/doc/examples/select_for_update.rb +18 -0
- data/doc/examples/select_from.rb +17 -0
- data/doc/examples/select_from_subselect.rb +20 -0
- data/doc/examples/select_group_by.rb +19 -0
- data/doc/examples/select_having.rb +20 -0
- data/doc/examples/select_join_on.rb +18 -0
- data/doc/examples/select_join_using.rb +18 -0
- data/doc/examples/select_limit.rb +19 -0
- data/doc/examples/select_natural_join.rb +18 -0
- data/doc/examples/select_offset.rb +19 -0
- data/doc/examples/select_order_by.rb +20 -0
- data/doc/examples/select_select.rb +30 -0
- data/doc/examples/select_select_alias.rb +30 -0
- data/doc/examples/select_select_expression.rb +31 -0
- data/doc/examples/select_select_literal.rb +24 -0
- data/doc/examples/select_union.rb +21 -0
- data/doc/examples/select_where_array.rb +18 -0
- data/doc/examples/select_where_in.rb +18 -0
- data/doc/examples/select_where_string.rb +18 -0
- data/doc/examples/simple.rb +34 -0
- data/doc/examples/transaction.rb +30 -0
- data/doc/examples/transaction_abort.rb +30 -0
- data/doc/examples/transaction_commit.rb +34 -0
- data/doc/examples/translate_substitute_values.rb +17 -0
- data/doc/examples/update.rb +32 -0
- data/doc/examples/update2.rb +44 -0
- data/doc/examples/update_only.rb +17 -0
- data/doc/examples/update_set.rb +17 -0
- data/doc/examples/update_set_array.rb +16 -0
- data/doc/examples/update_set_bytea.rb +16 -0
- data/doc/examples/update_set_expression.rb +16 -0
- data/doc/examples/update_set_subselect.rb +20 -0
- data/doc/examples/update_where.rb +17 -0
- data/doc/examples/use_prefix.rb +8 -0
- data/doc/examples/use_prefix2.rb +11 -0
- data/doc/index.html +31 -0
- data/doc/insertexamples.rb +9 -0
- data/doc/makemanual +4 -0
- data/doc/makerdoc +5 -0
- data/doc/manual.dbk +622 -0
- data/lib/sqlpostgres/Connection.rb +198 -0
- data/lib/sqlpostgres/Cursor.rb +157 -0
- data/lib/sqlpostgres/Delete.rb +67 -0
- data/lib/sqlpostgres/Exceptions.rb +15 -0
- data/lib/sqlpostgres/Insert.rb +279 -0
- data/lib/sqlpostgres/NullConnection.rb +22 -0
- data/lib/sqlpostgres/PgBit.rb +73 -0
- data/lib/sqlpostgres/PgBox.rb +37 -0
- data/lib/sqlpostgres/PgCidr.rb +21 -0
- data/lib/sqlpostgres/PgCircle.rb +75 -0
- data/lib/sqlpostgres/PgInet.rb +21 -0
- data/lib/sqlpostgres/PgInterval.rb +208 -0
- data/lib/sqlpostgres/PgLineSegment.rb +37 -0
- data/lib/sqlpostgres/PgMacAddr.rb +21 -0
- data/lib/sqlpostgres/PgPath.rb +64 -0
- data/lib/sqlpostgres/PgPoint.rb +65 -0
- data/lib/sqlpostgres/PgPolygon.rb +56 -0
- data/lib/sqlpostgres/PgTime.rb +77 -0
- data/lib/sqlpostgres/PgTimeWithTimeZone.rb +98 -0
- data/lib/sqlpostgres/PgTimestamp.rb +93 -0
- data/lib/sqlpostgres/PgTwoPoints.rb +54 -0
- data/lib/sqlpostgres/PgType.rb +34 -0
- data/lib/sqlpostgres/PgWrapper.rb +41 -0
- data/lib/sqlpostgres/Savepoint.rb +98 -0
- data/lib/sqlpostgres/Select.rb +855 -0
- data/lib/sqlpostgres/Transaction.rb +120 -0
- data/lib/sqlpostgres/Translate.rb +436 -0
- data/lib/sqlpostgres/Update.rb +188 -0
- data/lib/sqlpostgres.rb +67 -0
- data/test/Assert.rb +72 -0
- data/test/Connection.test.rb +246 -0
- data/test/Cursor.test.rb +190 -0
- data/test/Delete.test.rb +68 -0
- data/test/Insert.test.rb +123 -0
- data/test/MockPGconn.rb +62 -0
- data/test/NullConnection.test.rb +32 -0
- data/test/PgBit.test.rb +98 -0
- data/test/PgBox.test.rb +108 -0
- data/test/PgCidr.test.rb +61 -0
- data/test/PgCircle.test.rb +107 -0
- data/test/PgInet.test.rb +61 -0
- data/test/PgInterval.test.rb +180 -0
- data/test/PgLineSegment.test.rb +108 -0
- data/test/PgMacAddr.test.rb +61 -0
- data/test/PgPath.test.rb +106 -0
- data/test/PgPoint.test.rb +100 -0
- data/test/PgPolygon.test.rb +95 -0
- data/test/PgTime.test.rb +120 -0
- data/test/PgTimeWithTimeZone.test.rb +117 -0
- data/test/PgTimestamp.test.rb +134 -0
- data/test/RandomThings.rb +25 -0
- data/test/Savepoint.test.rb +286 -0
- data/test/Select.test.rb +930 -0
- data/test/Test.rb +62 -0
- data/test/TestConfig.rb +21 -0
- data/test/TestSetup.rb +13 -0
- data/test/TestUtil.rb +92 -0
- data/test/Transaction.test.rb +275 -0
- data/test/Translate.test.rb +354 -0
- data/test/Update.test.rb +227 -0
- data/test/roundtrip.test.rb +565 -0
- data/test/test +34 -0
- data/tools/exampleinserter/ExampleInserter.rb +177 -0
- data/tools/rdoc/ChangeLog +796 -0
- data/tools/rdoc/EXAMPLE.rb +48 -0
- data/tools/rdoc/MANIFEST +58 -0
- data/tools/rdoc/Makefile +27 -0
- data/tools/rdoc/NEW_FEATURES +226 -0
- data/tools/rdoc/README +390 -0
- data/tools/rdoc/ToDo +6 -0
- data/tools/rdoc/contrib/Index +6 -0
- data/tools/rdoc/contrib/xslfo/ChangeLog +181 -0
- data/tools/rdoc/contrib/xslfo/README +106 -0
- data/tools/rdoc/contrib/xslfo/TODO +10 -0
- data/tools/rdoc/contrib/xslfo/convert.xsl +151 -0
- data/tools/rdoc/contrib/xslfo/demo/README +21 -0
- data/tools/rdoc/contrib/xslfo/demo/rdocfo +99 -0
- data/tools/rdoc/contrib/xslfo/fcm.xsl +54 -0
- data/tools/rdoc/contrib/xslfo/files.xsl +62 -0
- data/tools/rdoc/contrib/xslfo/labeled-lists.xsl +66 -0
- data/tools/rdoc/contrib/xslfo/lists.xsl +44 -0
- data/tools/rdoc/contrib/xslfo/modules.xsl +152 -0
- data/tools/rdoc/contrib/xslfo/rdoc.xsl +75 -0
- data/tools/rdoc/contrib/xslfo/source.xsl +66 -0
- data/tools/rdoc/contrib/xslfo/styles.xsl +69 -0
- data/tools/rdoc/contrib/xslfo/tables.xsl +67 -0
- data/tools/rdoc/contrib/xslfo/utils.xsl +21 -0
- data/tools/rdoc/debian/changelog +33 -0
- data/tools/rdoc/debian/compat +1 -0
- data/tools/rdoc/debian/control +20 -0
- data/tools/rdoc/debian/copyright +10 -0
- data/tools/rdoc/debian/dirs +2 -0
- data/tools/rdoc/debian/docs +2 -0
- data/tools/rdoc/debian/rdoc.1 +252 -0
- data/tools/rdoc/debian/rdoc.manpages +1 -0
- data/tools/rdoc/debian/rdoc.pod +149 -0
- data/tools/rdoc/debian/rules +9 -0
- data/tools/rdoc/dot/dot.rb +255 -0
- data/tools/rdoc/etc/rdoc.dtd +203 -0
- data/tools/rdoc/install.rb +137 -0
- data/tools/rdoc/markup/install.rb +43 -0
- data/tools/rdoc/markup/sample/sample.rb +42 -0
- data/tools/rdoc/markup/simple_markup/fragments.rb +323 -0
- data/tools/rdoc/markup/simple_markup/inline.rb +348 -0
- data/tools/rdoc/markup/simple_markup/lines.rb +147 -0
- data/tools/rdoc/markup/simple_markup/preprocess.rb +68 -0
- data/tools/rdoc/markup/simple_markup/to_html.rb +281 -0
- data/tools/rdoc/markup/simple_markup.rb +474 -0
- data/tools/rdoc/markup/test/AllTests.rb +2 -0
- data/tools/rdoc/markup/test/TestInline.rb +151 -0
- data/tools/rdoc/markup/test/TestParse.rb +411 -0
- data/tools/rdoc/rdoc/code_objects.rb +536 -0
- data/tools/rdoc/rdoc/diagram.rb +331 -0
- data/tools/rdoc/rdoc/generators/chm_generator.rb +112 -0
- data/tools/rdoc/rdoc/generators/html_generator.rb +1268 -0
- data/tools/rdoc/rdoc/generators/template/chm/chm.rb +86 -0
- data/tools/rdoc/rdoc/generators/template/html/html.rb +705 -0
- data/tools/rdoc/rdoc/generators/template/html/kilmer.rb +377 -0
- data/tools/rdoc/rdoc/generators/template/xml/rdf.rb +110 -0
- data/tools/rdoc/rdoc/generators/template/xml/xml.rb +110 -0
- data/tools/rdoc/rdoc/generators/xml_generator.rb +130 -0
- data/tools/rdoc/rdoc/options.rb +451 -0
- data/tools/rdoc/rdoc/parsers/parse_c.rb +287 -0
- data/tools/rdoc/rdoc/parsers/parse_f95.rb +118 -0
- data/tools/rdoc/rdoc/parsers/parse_rb.rb +2311 -0
- data/tools/rdoc/rdoc/parsers/parse_simple.rb +37 -0
- data/tools/rdoc/rdoc/parsers/parserfactory.rb +75 -0
- data/tools/rdoc/rdoc/rdoc.rb +219 -0
- data/tools/rdoc/rdoc/template.rb +234 -0
- data/tools/rdoc/rdoc/tokenstream.rb +25 -0
- data/tools/rdoc/rdoc.rb +9 -0
- metadata +291 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
module SqlPostgres
|
2
|
+
|
3
|
+
# This class handles an SQL transaction.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
#** example: transaction
|
7
|
+
# Transaction.new(connection) do
|
8
|
+
#
|
9
|
+
# insert = Insert.new('foo', connection)
|
10
|
+
# insert.insert('i', 1)
|
11
|
+
# insert.exec
|
12
|
+
#
|
13
|
+
# insert = Insert.new('foo', connection)
|
14
|
+
# insert.insert('i', 2)
|
15
|
+
# insert.exec
|
16
|
+
#
|
17
|
+
# end
|
18
|
+
#**
|
19
|
+
|
20
|
+
class Transaction
|
21
|
+
|
22
|
+
# Create an SQL transaction, yield, and then end the transaction.
|
23
|
+
# If an exception occurs, the transaction is aborted.
|
24
|
+
#
|
25
|
+
# If no connection is given, then the default connection is used.
|
26
|
+
|
27
|
+
def initialize(connection = Connection.default)
|
28
|
+
@state = :open
|
29
|
+
@finished = false
|
30
|
+
@connection = connection
|
31
|
+
@connection.exec("begin transaction")
|
32
|
+
begin
|
33
|
+
result = yield(self)
|
34
|
+
commit
|
35
|
+
result
|
36
|
+
rescue Exception
|
37
|
+
abort
|
38
|
+
raise
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Commit this transaction. This is done for you unless an
|
43
|
+
# exception occurs within the block you passed to "new". Call
|
44
|
+
# this when you want to commit the transaction before raising an
|
45
|
+
# exception -- in other words, when you want to keep your database
|
46
|
+
# changes even though an exception is about to occur.
|
47
|
+
#
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
#** example: transaction_commit
|
51
|
+
# begin
|
52
|
+
# Transaction.new(connection) do |transaction|
|
53
|
+
# insert = Insert.new('foo', connection)
|
54
|
+
# insert.insert('i', 1)
|
55
|
+
# insert.exec
|
56
|
+
# transaction.commit
|
57
|
+
# raise
|
58
|
+
# end
|
59
|
+
# rescue Exception => e
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# select = Select.new(connection)
|
63
|
+
# select.select('i')
|
64
|
+
# select.from('foo')
|
65
|
+
# p select.exec # [{"i"=>1}]
|
66
|
+
#**
|
67
|
+
|
68
|
+
def commit
|
69
|
+
unless @finished
|
70
|
+
do_commit
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Abort this transaction. This is done for you when an exception
|
75
|
+
# occurs within the block you passed to "new". Call this when you
|
76
|
+
# want to abort a transaction without throwing an exception.
|
77
|
+
#
|
78
|
+
# Example:
|
79
|
+
#
|
80
|
+
#** example: transaction_abort
|
81
|
+
# Transaction.new(connection) do |transaction|
|
82
|
+
# insert = Insert.new('foo', connection)
|
83
|
+
# insert.insert('i', 1)
|
84
|
+
# insert.exec
|
85
|
+
# transaction.abort
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# select = Select.new(connection)
|
89
|
+
# select.select('i')
|
90
|
+
# select.from('foo')
|
91
|
+
# p select.exec # []
|
92
|
+
#**
|
93
|
+
|
94
|
+
def abort
|
95
|
+
unless @finished
|
96
|
+
do_abort
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def do_commit
|
103
|
+
@connection.exec("end transaction")
|
104
|
+
@finished = true
|
105
|
+
end
|
106
|
+
|
107
|
+
def do_abort
|
108
|
+
@connection.exec("abort transaction")
|
109
|
+
@finished = true
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# Local Variables:
|
117
|
+
# tab-width: 2
|
118
|
+
# ruby-indent-level: 2
|
119
|
+
# indent-tabs-mode: nil
|
120
|
+
# End:
|
@@ -0,0 +1,436 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
module SqlPostgres
|
4
|
+
|
5
|
+
# Translation functions. These are internal and should not be used
|
6
|
+
# by clients unless you want extra work when they change.
|
7
|
+
|
8
|
+
module Translate
|
9
|
+
|
10
|
+
# Turn a Ruby object into an SQL string.
|
11
|
+
#
|
12
|
+
# The conversion depends upon the type of thing:
|
13
|
+
#
|
14
|
+
# [[String, ...]]
|
15
|
+
# An arbitrary expression, possibly with value substitution.
|
16
|
+
# Converted by calling #substitute_values
|
17
|
+
#
|
18
|
+
# Examples:
|
19
|
+
# ['foo'] -> 'foo'
|
20
|
+
# ['%s + %s', 1, 1.414] -> '1 + 1.414'
|
21
|
+
#
|
22
|
+
# [[:in, ...]]
|
23
|
+
# A list of values that will be converted by recursively
|
24
|
+
# calling escape_sql, separated with commas, and surrounded
|
25
|
+
# by parentheses.
|
26
|
+
#
|
27
|
+
# Examples:
|
28
|
+
# [:in, 1, 2] -> "(1, 2)"
|
29
|
+
# [:in, 'foo', 'bar'] => "('foo', 'bar')"
|
30
|
+
#
|
31
|
+
# [String]
|
32
|
+
# Backslashes and single-quotes are escaped; the resulting
|
33
|
+
# string is then enclosed in single-quotes.
|
34
|
+
#
|
35
|
+
# Examples:
|
36
|
+
# "foo" -> "'foo'"
|
37
|
+
# "fool's gold' -> %q"'fool\\'s gold'"
|
38
|
+
# 'foo\\bar' -> %q"'foo\\\\bar'"
|
39
|
+
#
|
40
|
+
# [false]
|
41
|
+
# Converted to "false"
|
42
|
+
#
|
43
|
+
# [true]
|
44
|
+
# Converted to "true"
|
45
|
+
#
|
46
|
+
# [Integer]
|
47
|
+
# Converted to a string
|
48
|
+
#
|
49
|
+
# Examples:
|
50
|
+
# 123 -> "123"
|
51
|
+
# -2 -> "-2"
|
52
|
+
#
|
53
|
+
# [BigDecimal]
|
54
|
+
# Converted ot a a string
|
55
|
+
#
|
56
|
+
# Examples:
|
57
|
+
# BigDecimal("123.456789012345") -> "123.456789012345"
|
58
|
+
#
|
59
|
+
# [Float]
|
60
|
+
# Converted to a string with 15 digits of precision, using exponential
|
61
|
+
# notation of necessary.
|
62
|
+
#
|
63
|
+
# Examples:
|
64
|
+
# 0 -> "0"
|
65
|
+
# -1 -> "-1"
|
66
|
+
# 3.1415926535898 -> "3.1415926535898"
|
67
|
+
# 1e100 -> "1e+100"
|
68
|
+
#
|
69
|
+
# [Time]
|
70
|
+
# Converted to a timestamp with microseconds.
|
71
|
+
#
|
72
|
+
# Examples:
|
73
|
+
# Time.local(2000, 1, 2, 3, 4, 5, 6) ->
|
74
|
+
# "timestamp '2000-01-02 03:04:05.000006'"
|
75
|
+
#
|
76
|
+
# [:default]
|
77
|
+
# Converted to "default"
|
78
|
+
#
|
79
|
+
# [nil]
|
80
|
+
# Converted to "null"
|
81
|
+
#
|
82
|
+
# [Select]
|
83
|
+
# The statement method is called to get the SQL, which is then
|
84
|
+
# wrapped in parentheses.
|
85
|
+
#
|
86
|
+
# Example, supposing that select.statement is "select 1 as i":
|
87
|
+
# select -> "(select 1 as i)"
|
88
|
+
#
|
89
|
+
# [anything else]
|
90
|
+
# Treated as a String (after calling to_s on it)
|
91
|
+
|
92
|
+
def escape_sql(thing)
|
93
|
+
return "null" if thing.nil?
|
94
|
+
if thing.is_a?(Array)
|
95
|
+
substitute_values(thing)
|
96
|
+
elsif thing.respond_to?(:to_sql)
|
97
|
+
thing.to_sql
|
98
|
+
elsif thing.is_a?(Time)
|
99
|
+
"timestamp '#{timeToSql(thing)}'"
|
100
|
+
elsif thing.is_a?(DateTime)
|
101
|
+
"timestamp with time zone '#{datetime_to_sql(thing)}'"
|
102
|
+
elsif thing.is_a?(Integer)
|
103
|
+
thing.to_s
|
104
|
+
elsif thing.is_a?(Float)
|
105
|
+
"%.14g" % thing
|
106
|
+
elsif thing.is_a?(Date)
|
107
|
+
"date '#{thing}'"
|
108
|
+
elsif thing.is_a?(BigDecimal)
|
109
|
+
thing.to_s('f')
|
110
|
+
elsif thing == false
|
111
|
+
"false"
|
112
|
+
elsif thing == true
|
113
|
+
"true"
|
114
|
+
elsif thing == :default
|
115
|
+
"default"
|
116
|
+
elsif thing.respond_to?(:statement)
|
117
|
+
"(#{thing.statement})"
|
118
|
+
else
|
119
|
+
string_to_sql(thing.to_s, '\\')
|
120
|
+
end
|
121
|
+
end
|
122
|
+
module_function :escape_sql
|
123
|
+
|
124
|
+
# Convert an arbitrary expression to SQL, possibly with value
|
125
|
+
# substitution. expression is an array.
|
126
|
+
#
|
127
|
+
# If the first element of the array is a String, then it is the
|
128
|
+
# format specificer The format specifier contains a %s for each
|
129
|
+
# value. The remainin items, if they exist, are values. Each
|
130
|
+
# value is turned into a string by calling escape_sql and is then
|
131
|
+
# substituted for a %s in the format specifier.
|
132
|
+
#
|
133
|
+
# If the first element of the array is :in, then the remaining
|
134
|
+
# items are to be formatted using escape_sql, separated by commas,
|
135
|
+
# and surrounded by parentheses.
|
136
|
+
#
|
137
|
+
# Examples:
|
138
|
+
#** example: translate_substitute_values
|
139
|
+
# p Translate.substitute_values(['foo']) # "foo"
|
140
|
+
# p Translate.substitute_values(['%s + %s', 1, 2]) # "1 + 2"
|
141
|
+
# p Translate.substitute_values([:in, 1, 2]) # "(1, 2)"
|
142
|
+
# p Translate.substitute_values([:in, 'foo', 'bar']) # "(E'foo',
|
143
|
+
# # E'bar')"
|
144
|
+
#**
|
145
|
+
|
146
|
+
def substitute_values(expression)
|
147
|
+
if expression.is_a?(Array)
|
148
|
+
pieces = expression[1..-1].collect do |value|
|
149
|
+
escape_sql(value)
|
150
|
+
end
|
151
|
+
if expression.first == :in
|
152
|
+
"(#{pieces.join(', ')})"
|
153
|
+
else
|
154
|
+
expression[0] % pieces
|
155
|
+
end
|
156
|
+
else
|
157
|
+
expression
|
158
|
+
end
|
159
|
+
end
|
160
|
+
module_function :substitute_values
|
161
|
+
|
162
|
+
# Escape an array to be inserted into a Postgres array column.
|
163
|
+
# Array columns are a Postgres extension.
|
164
|
+
|
165
|
+
def escape_array(a)
|
166
|
+
if a.is_a?(Array)
|
167
|
+
if a.empty?
|
168
|
+
"'{}'"
|
169
|
+
else
|
170
|
+
pieces = a.collect do |e|
|
171
|
+
escape_array(e)
|
172
|
+
end
|
173
|
+
"ARRAY[#{pieces.join(', ')}]"
|
174
|
+
end
|
175
|
+
else
|
176
|
+
escape_sql(a)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
module_function :escape_array
|
180
|
+
|
181
|
+
# Escape an array to be inserted into a Posgres bytea[] column.
|
182
|
+
# Array columns (and bytea columns) are a Postgres extension.
|
183
|
+
|
184
|
+
def escape_bytea_array(a)
|
185
|
+
escape_bytea_quote(a) do |e|
|
186
|
+
e.
|
187
|
+
gsub(/\\/, '\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\').
|
188
|
+
gsub(/\000/, "\\\\\\\\\\\\\\\\000")
|
189
|
+
end
|
190
|
+
end
|
191
|
+
module_function :escape_bytea_array
|
192
|
+
|
193
|
+
def escape_bytea_quote(a, &escaper)
|
194
|
+
return "null" if a.nil?
|
195
|
+
"'#{escape_array_noquote(a, &escaper)}'"
|
196
|
+
end
|
197
|
+
module_function :escape_bytea_quote
|
198
|
+
private_class_method :escape_bytea_quote
|
199
|
+
|
200
|
+
def escape_array_noquote(a, &escaper)
|
201
|
+
pieces = Array(a).map do |e|
|
202
|
+
if e.is_a?(Array)
|
203
|
+
escape_array_noquote(e, &escaper)
|
204
|
+
else
|
205
|
+
escaped = escaper.call(e.to_s).
|
206
|
+
gsub(/'/, "\\\\'").
|
207
|
+
gsub(/"/, "\\\\\\\"")
|
208
|
+
'"' + escaped + '"'
|
209
|
+
end
|
210
|
+
end
|
211
|
+
"{" + pieces.join(',') + "}"
|
212
|
+
end
|
213
|
+
module_function :escape_array_noquote
|
214
|
+
private_class_method :escape_array_noquote
|
215
|
+
|
216
|
+
# Translate a Postgres string representation of an array into a
|
217
|
+
# Ruby array of strings.
|
218
|
+
|
219
|
+
def sql_to_array(s)
|
220
|
+
begin
|
221
|
+
a, t = sql_to_array2(s)
|
222
|
+
raise ArgumentError unless t.empty?
|
223
|
+
a
|
224
|
+
rescue ArgumentError
|
225
|
+
raise ArgumentError, s.inspect
|
226
|
+
end
|
227
|
+
end
|
228
|
+
module_function :sql_to_array
|
229
|
+
|
230
|
+
def sql_to_array2(s)
|
231
|
+
if s !~ /\A\{/m
|
232
|
+
raise ArgumentError
|
233
|
+
else
|
234
|
+
t = $'
|
235
|
+
a = []
|
236
|
+
loop do
|
237
|
+
case t
|
238
|
+
when /\A\},?/
|
239
|
+
return [a, $']
|
240
|
+
when /\A\{/
|
241
|
+
e, t = sql_to_array2(t)
|
242
|
+
a << e
|
243
|
+
when /\A([^"][^\},]*),?/m
|
244
|
+
t = $' || ""
|
245
|
+
a << $1
|
246
|
+
when /\A"((?:[^\\]|\\\\|\\")*?)",?/m
|
247
|
+
t = $' || ""
|
248
|
+
a << $1.gsub(/\\\\/, "\\").gsub(/\\"/, '"')
|
249
|
+
else
|
250
|
+
raise ArgumentError
|
251
|
+
end
|
252
|
+
end
|
253
|
+
a
|
254
|
+
end
|
255
|
+
end
|
256
|
+
module_function :sql_to_array2
|
257
|
+
private_class_method :sql_to_array2
|
258
|
+
|
259
|
+
# Escape a string to be inserted into a bytea (byte array) column.
|
260
|
+
#
|
261
|
+
# [s]
|
262
|
+
# The value to convert. Should be one of:
|
263
|
+
# * String
|
264
|
+
# * nil
|
265
|
+
# * :default
|
266
|
+
#
|
267
|
+
# The following characters get converted to mega-backslashed octal:
|
268
|
+
# \x00-\x1f
|
269
|
+
# '
|
270
|
+
# \
|
271
|
+
# \x7f-\xff
|
272
|
+
|
273
|
+
def escape_bytea(s)
|
274
|
+
return "null" if s.nil?
|
275
|
+
return "default" if s == :default
|
276
|
+
"E'" + PGconn.escape_bytea(Array(s).join) + "'"
|
277
|
+
end
|
278
|
+
module_function :escape_bytea
|
279
|
+
|
280
|
+
# Unescape octal escape sequences, turning them back into bytes.
|
281
|
+
|
282
|
+
def unescape_octal_escapes(s)
|
283
|
+
s.gsub(/\\(\d{3})/) do
|
284
|
+
$1.oct.chr
|
285
|
+
end.gsub(/\\\\/, '\\')
|
286
|
+
end
|
287
|
+
module_function :unescape_octal_escapes
|
288
|
+
|
289
|
+
# Unescape a bytea string read from postgres.
|
290
|
+
|
291
|
+
def unescape_bytea(s)
|
292
|
+
if s.respond_to?(:force_encoding)
|
293
|
+
s = s.force_encoding("ASCII-8BIT")
|
294
|
+
end
|
295
|
+
s.gsub(/\\(\\|[0-3][0-7][0-7])/) do
|
296
|
+
if $1 == "\\"
|
297
|
+
"\\"
|
298
|
+
else
|
299
|
+
$1.oct.chr
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
module_function :unescape_bytea
|
304
|
+
|
305
|
+
# Unescape a text string read from postges.
|
306
|
+
|
307
|
+
def unescape_text(s)
|
308
|
+
unescape_bytea(s)
|
309
|
+
end
|
310
|
+
module_function :unescape_text
|
311
|
+
|
312
|
+
# Convert a time to SQL format, including microseconds:
|
313
|
+
# (YYYY-mm-dd HH:MM:SS.uuuuuu)
|
314
|
+
|
315
|
+
def timeToSql(time)
|
316
|
+
time.strftime("%Y-%m-%d %H:%M:%S.") + ("%06d" % time.usec)
|
317
|
+
end
|
318
|
+
module_function :timeToSql
|
319
|
+
|
320
|
+
# Convert a datetime to Postgres format (ie "2003-10-18
|
321
|
+
# 11:30:24.000000-07")
|
322
|
+
|
323
|
+
def datetime_to_sql(d)
|
324
|
+
d.strftime("%Y-%m-%d %H:%M:%S%z").gsub(/Z$/, "+0000")
|
325
|
+
end
|
326
|
+
module_function :datetime_to_sql
|
327
|
+
|
328
|
+
# Convert a date from Postgres format (ie "2003-10-18") to a Date
|
329
|
+
|
330
|
+
def sql_to_date(s)
|
331
|
+
Date.parse(s)
|
332
|
+
end
|
333
|
+
module_function :sql_to_date
|
334
|
+
|
335
|
+
# Convert a time with timezone from Postgres format (ie
|
336
|
+
# "2003-10-18 11:30:24-07") to a DateTime.
|
337
|
+
|
338
|
+
def sql_to_datetime(s)
|
339
|
+
DateTime.parse(s)
|
340
|
+
end
|
341
|
+
module_function :sql_to_datetime
|
342
|
+
|
343
|
+
# Escape a "char" type (the special Postgres internal type used in
|
344
|
+
# system tables).
|
345
|
+
#
|
346
|
+
# [s]
|
347
|
+
# A String of length 1.
|
348
|
+
|
349
|
+
def escape_qchar(s)
|
350
|
+
if s.nil?
|
351
|
+
"null"
|
352
|
+
else
|
353
|
+
"E'" + escape_char(s[0].ord) + "'"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
module_function :escape_qchar
|
357
|
+
|
358
|
+
# # Escape a "char"[] type (the special Postgres internal type used
|
359
|
+
# # in system tables).
|
360
|
+
# #
|
361
|
+
# # [a]
|
362
|
+
# # An array (possibly nested) of String of length 1.
|
363
|
+
|
364
|
+
# def escape_qchar_array(a)
|
365
|
+
# escape_bytea_array(a)
|
366
|
+
# end
|
367
|
+
# module_function :escape_qchar_array
|
368
|
+
|
369
|
+
# Unescape a "char" type. This is a special internal type (yes, the
|
370
|
+
# quotes are part of the type name).
|
371
|
+
#
|
372
|
+
# [s]
|
373
|
+
# A String of length 1. If empty, it really means "\000".
|
374
|
+
|
375
|
+
def unescape_qchar(s)
|
376
|
+
if s.empty?
|
377
|
+
"\000"
|
378
|
+
else
|
379
|
+
return s
|
380
|
+
end
|
381
|
+
end
|
382
|
+
module_function :unescape_qchar
|
383
|
+
|
384
|
+
# Escape a character, converting non-printable (0x0-0x1f, 0x7f-0xff),
|
385
|
+
# backslash, and single-quote into an octal escape sequence.
|
386
|
+
#
|
387
|
+
# [c]
|
388
|
+
# The character code to convert (an integer between 0 and 255)
|
389
|
+
#
|
390
|
+
# [backslahes]
|
391
|
+
# The backslashes to use in the escape sequence
|
392
|
+
|
393
|
+
def escape_char(c, backslashes = '\\')
|
394
|
+
"#{backslashes}%03o" % c
|
395
|
+
end
|
396
|
+
module_function :escape_char
|
397
|
+
private_class_method :escape_char
|
398
|
+
|
399
|
+
# Escape a string, converting non-printable (\x0-\x1f, \x7f-\xff),
|
400
|
+
# backshlash, and single-quote into octal escape sequences and/or
|
401
|
+
# unicode.
|
402
|
+
#
|
403
|
+
# [thing]
|
404
|
+
# The string to convert
|
405
|
+
#
|
406
|
+
# [backslashes]
|
407
|
+
# The backslashes to use in the escape sequence.
|
408
|
+
|
409
|
+
def string_to_sql(thing, backslashes)
|
410
|
+
"E'" + thing.gsub(/[\x0-\x1f\x80-\xff'\\]/) do |c|
|
411
|
+
escape_char(c[0].ord, backslashes)
|
412
|
+
end + "'"
|
413
|
+
end
|
414
|
+
module_function :string_to_sql
|
415
|
+
private_class_method :string_to_sql
|
416
|
+
|
417
|
+
def deep_collect(a)
|
418
|
+
if a.is_a?(Array)
|
419
|
+
a.collect do |e|
|
420
|
+
deep_collect(e) do |v| yield(v) end
|
421
|
+
end
|
422
|
+
else
|
423
|
+
yield(a)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
module_function :deep_collect
|
427
|
+
|
428
|
+
end
|
429
|
+
|
430
|
+
end
|
431
|
+
|
432
|
+
# Local Variables:
|
433
|
+
# tab-width: 2
|
434
|
+
# ruby-indent-level: 2
|
435
|
+
# indent-tabs-mode: nil
|
436
|
+
# End:
|