activerecord-jdbc-adapter-ficoh 1.3.21-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +462 -0
  4. data/.yardopts +4 -0
  5. data/Appraisals +36 -0
  6. data/CONTRIBUTING.md +49 -0
  7. data/Gemfile +68 -0
  8. data/History.md +1191 -0
  9. data/LICENSE.txt +25 -0
  10. data/README.md +277 -0
  11. data/RUNNING_TESTS.md +88 -0
  12. data/Rakefile +298 -0
  13. data/Rakefile.jdbc +20 -0
  14. data/activerecord-jdbc-adapter.gemspec +63 -0
  15. data/lib/active_record/connection_adapters/as400_adapter.rb +2 -0
  16. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -0
  17. data/lib/active_record/connection_adapters/derby_adapter.rb +1 -0
  18. data/lib/active_record/connection_adapters/firebird_adapter.rb +1 -0
  19. data/lib/active_record/connection_adapters/h2_adapter.rb +1 -0
  20. data/lib/active_record/connection_adapters/hsqldb_adapter.rb +1 -0
  21. data/lib/active_record/connection_adapters/informix_adapter.rb +1 -0
  22. data/lib/active_record/connection_adapters/jdbc_adapter.rb +1 -0
  23. data/lib/active_record/connection_adapters/jndi_adapter.rb +1 -0
  24. data/lib/active_record/connection_adapters/mariadb_adapter.rb +1 -0
  25. data/lib/active_record/connection_adapters/mssql_adapter.rb +1 -0
  26. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
  28. data/lib/active_record/connection_adapters/oracle_adapter.rb +1 -0
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -0
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
  31. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1 -0
  32. data/lib/activerecord-jdbc-adapter.rb +1 -0
  33. data/lib/arel/visitors/compat.rb +64 -0
  34. data/lib/arel/visitors/db2.rb +137 -0
  35. data/lib/arel/visitors/derby.rb +112 -0
  36. data/lib/arel/visitors/firebird.rb +79 -0
  37. data/lib/arel/visitors/h2.rb +25 -0
  38. data/lib/arel/visitors/hsqldb.rb +32 -0
  39. data/lib/arel/visitors/postgresql_jdbc.rb +6 -0
  40. data/lib/arel/visitors/sql_server.rb +225 -0
  41. data/lib/arel/visitors/sql_server/ng42.rb +293 -0
  42. data/lib/arjdbc.rb +22 -0
  43. data/lib/arjdbc/db2.rb +4 -0
  44. data/lib/arjdbc/db2/adapter.rb +802 -0
  45. data/lib/arjdbc/db2/as400.rb +137 -0
  46. data/lib/arjdbc/db2/column.rb +177 -0
  47. data/lib/arjdbc/db2/connection_methods.rb +45 -0
  48. data/lib/arjdbc/derby.rb +3 -0
  49. data/lib/arjdbc/derby/active_record_patch.rb +13 -0
  50. data/lib/arjdbc/derby/adapter.rb +567 -0
  51. data/lib/arjdbc/derby/connection_methods.rb +16 -0
  52. data/lib/arjdbc/derby/schema_creation.rb +15 -0
  53. data/lib/arjdbc/discover.rb +104 -0
  54. data/lib/arjdbc/firebird.rb +4 -0
  55. data/lib/arjdbc/firebird/adapter.rb +468 -0
  56. data/lib/arjdbc/firebird/connection_methods.rb +20 -0
  57. data/lib/arjdbc/h2.rb +3 -0
  58. data/lib/arjdbc/h2/adapter.rb +335 -0
  59. data/lib/arjdbc/h2/connection_methods.rb +22 -0
  60. data/lib/arjdbc/hsqldb.rb +3 -0
  61. data/lib/arjdbc/hsqldb/adapter.rb +304 -0
  62. data/lib/arjdbc/hsqldb/connection_methods.rb +23 -0
  63. data/lib/arjdbc/hsqldb/explain_support.rb +35 -0
  64. data/lib/arjdbc/hsqldb/schema_creation.rb +11 -0
  65. data/lib/arjdbc/informix.rb +5 -0
  66. data/lib/arjdbc/informix/adapter.rb +160 -0
  67. data/lib/arjdbc/informix/connection_methods.rb +9 -0
  68. data/lib/arjdbc/jdbc.rb +62 -0
  69. data/lib/arjdbc/jdbc/adapter.rb +997 -0
  70. data/lib/arjdbc/jdbc/adapter_require.rb +46 -0
  71. data/lib/arjdbc/jdbc/arel_support.rb +149 -0
  72. data/lib/arjdbc/jdbc/base_ext.rb +34 -0
  73. data/lib/arjdbc/jdbc/callbacks.rb +52 -0
  74. data/lib/arjdbc/jdbc/column.rb +83 -0
  75. data/lib/arjdbc/jdbc/connection.rb +26 -0
  76. data/lib/arjdbc/jdbc/connection_methods.rb +59 -0
  77. data/lib/arjdbc/jdbc/driver.rb +44 -0
  78. data/lib/arjdbc/jdbc/error.rb +75 -0
  79. data/lib/arjdbc/jdbc/extension.rb +69 -0
  80. data/lib/arjdbc/jdbc/java.rb +13 -0
  81. data/lib/arjdbc/jdbc/type_cast.rb +154 -0
  82. data/lib/arjdbc/jdbc/type_converter.rb +142 -0
  83. data/lib/arjdbc/mssql.rb +7 -0
  84. data/lib/arjdbc/mssql/adapter.rb +822 -0
  85. data/lib/arjdbc/mssql/column.rb +207 -0
  86. data/lib/arjdbc/mssql/connection_methods.rb +72 -0
  87. data/lib/arjdbc/mssql/explain_support.rb +99 -0
  88. data/lib/arjdbc/mssql/limit_helpers.rb +231 -0
  89. data/lib/arjdbc/mssql/lock_methods.rb +77 -0
  90. data/lib/arjdbc/mssql/types.rb +343 -0
  91. data/lib/arjdbc/mssql/utils.rb +82 -0
  92. data/lib/arjdbc/mysql.rb +3 -0
  93. data/lib/arjdbc/mysql/adapter.rb +998 -0
  94. data/lib/arjdbc/mysql/bulk_change_table.rb +150 -0
  95. data/lib/arjdbc/mysql/column.rb +167 -0
  96. data/lib/arjdbc/mysql/connection_methods.rb +137 -0
  97. data/lib/arjdbc/mysql/explain_support.rb +82 -0
  98. data/lib/arjdbc/mysql/schema_creation.rb +58 -0
  99. data/lib/arjdbc/oracle.rb +4 -0
  100. data/lib/arjdbc/oracle/adapter.rb +968 -0
  101. data/lib/arjdbc/oracle/column.rb +136 -0
  102. data/lib/arjdbc/oracle/connection_methods.rb +21 -0
  103. data/lib/arjdbc/postgresql.rb +3 -0
  104. data/lib/arjdbc/postgresql/_bc_time_cast_patch.rb +21 -0
  105. data/lib/arjdbc/postgresql/adapter.rb +1498 -0
  106. data/lib/arjdbc/postgresql/base/array_parser.rb +95 -0
  107. data/lib/arjdbc/postgresql/base/oid.rb +412 -0
  108. data/lib/arjdbc/postgresql/base/pgconn.rb +8 -0
  109. data/lib/arjdbc/postgresql/base/schema_definitions.rb +132 -0
  110. data/lib/arjdbc/postgresql/column.rb +640 -0
  111. data/lib/arjdbc/postgresql/connection_methods.rb +44 -0
  112. data/lib/arjdbc/postgresql/explain_support.rb +53 -0
  113. data/lib/arjdbc/postgresql/oid/bytea.rb +3 -0
  114. data/lib/arjdbc/postgresql/oid_types.rb +265 -0
  115. data/lib/arjdbc/postgresql/schema_creation.rb +60 -0
  116. data/lib/arjdbc/railtie.rb +11 -0
  117. data/lib/arjdbc/sqlite3.rb +3 -0
  118. data/lib/arjdbc/sqlite3/adapter.rb +654 -0
  119. data/lib/arjdbc/sqlite3/connection_methods.rb +36 -0
  120. data/lib/arjdbc/sqlite3/explain_support.rb +29 -0
  121. data/lib/arjdbc/sybase.rb +2 -0
  122. data/lib/arjdbc/sybase/adapter.rb +47 -0
  123. data/lib/arjdbc/tasks.rb +13 -0
  124. data/lib/arjdbc/tasks/database_tasks.rb +66 -0
  125. data/lib/arjdbc/tasks/databases.rake +91 -0
  126. data/lib/arjdbc/tasks/databases3.rake +239 -0
  127. data/lib/arjdbc/tasks/databases4.rake +39 -0
  128. data/lib/arjdbc/tasks/db2_database_tasks.rb +104 -0
  129. data/lib/arjdbc/tasks/derby_database_tasks.rb +95 -0
  130. data/lib/arjdbc/tasks/h2_database_tasks.rb +31 -0
  131. data/lib/arjdbc/tasks/hsqldb_database_tasks.rb +70 -0
  132. data/lib/arjdbc/tasks/jdbc_database_tasks.rb +169 -0
  133. data/lib/arjdbc/tasks/mssql_database_tasks.rb +46 -0
  134. data/lib/arjdbc/tasks/oracle/enhanced_structure_dump.rb +297 -0
  135. data/lib/arjdbc/tasks/oracle_database_tasks.rb +65 -0
  136. data/lib/arjdbc/util/quoted_cache.rb +60 -0
  137. data/lib/arjdbc/util/serialized_attributes.rb +98 -0
  138. data/lib/arjdbc/util/table_copier.rb +108 -0
  139. data/lib/arjdbc/version.rb +8 -0
  140. data/lib/generators/jdbc/USAGE +9 -0
  141. data/lib/generators/jdbc/jdbc_generator.rb +17 -0
  142. data/pom.xml +285 -0
  143. data/rails_generators/jdbc_generator.rb +15 -0
  144. data/rails_generators/templates/config/initializers/jdbc.rb +10 -0
  145. data/rails_generators/templates/lib/tasks/jdbc.rake +11 -0
  146. data/rakelib/01-tomcat.rake +51 -0
  147. data/rakelib/02-test.rake +151 -0
  148. data/rakelib/bundler_ext.rb +11 -0
  149. data/rakelib/db.rake +58 -0
  150. data/rakelib/rails.rake +77 -0
  151. data/src/java/arjdbc/ArJdbcModule.java +288 -0
  152. data/src/java/arjdbc/db2/DB2Module.java +77 -0
  153. data/src/java/arjdbc/db2/DB2RubyJdbcConnection.java +128 -0
  154. data/src/java/arjdbc/derby/DerbyModule.java +180 -0
  155. data/src/java/arjdbc/derby/DerbyRubyJdbcConnection.java +153 -0
  156. data/src/java/arjdbc/firebird/FirebirdRubyJdbcConnection.java +190 -0
  157. data/src/java/arjdbc/h2/H2Module.java +50 -0
  158. data/src/java/arjdbc/h2/H2RubyJdbcConnection.java +86 -0
  159. data/src/java/arjdbc/hsqldb/HSQLDBModule.java +74 -0
  160. data/src/java/arjdbc/informix/InformixRubyJdbcConnection.java +76 -0
  161. data/src/java/arjdbc/jdbc/AdapterJavaService.java +43 -0
  162. data/src/java/arjdbc/jdbc/Callable.java +44 -0
  163. data/src/java/arjdbc/jdbc/ConnectionFactory.java +77 -0
  164. data/src/java/arjdbc/jdbc/DataSourceConnectionFactory.java +156 -0
  165. data/src/java/arjdbc/jdbc/DriverConnectionFactory.java +63 -0
  166. data/src/java/arjdbc/jdbc/DriverWrapper.java +128 -0
  167. data/src/java/arjdbc/jdbc/JdbcConnectionFactory.java +32 -0
  168. data/src/java/arjdbc/jdbc/RubyJdbcConnection.java +4541 -0
  169. data/src/java/arjdbc/jdbc/SQLBlock.java +54 -0
  170. data/src/java/arjdbc/jdbc/WithResultSet.java +37 -0
  171. data/src/java/arjdbc/mssql/MSSQLModule.java +91 -0
  172. data/src/java/arjdbc/mssql/MSSQLRubyJdbcConnection.java +193 -0
  173. data/src/java/arjdbc/mysql/MySQLModule.java +140 -0
  174. data/src/java/arjdbc/mysql/MySQLRubyJdbcConnection.java +456 -0
  175. data/src/java/arjdbc/oracle/OracleModule.java +81 -0
  176. data/src/java/arjdbc/oracle/OracleRubyJdbcConnection.java +477 -0
  177. data/src/java/arjdbc/postgresql/ByteaUtils.java +171 -0
  178. data/src/java/arjdbc/postgresql/DriverImplementation.java +78 -0
  179. data/src/java/arjdbc/postgresql/PGDriverImplementation.java +535 -0
  180. data/src/java/arjdbc/postgresql/PostgreSQLModule.java +189 -0
  181. data/src/java/arjdbc/postgresql/PostgreSQLRubyJdbcConnection.java +489 -0
  182. data/src/java/arjdbc/sqlite3/SQLite3Module.java +93 -0
  183. data/src/java/arjdbc/sqlite3/SQLite3RubyJdbcConnection.java +405 -0
  184. data/src/java/arjdbc/util/CallResultSet.java +826 -0
  185. data/src/java/arjdbc/util/DateTimeUtils.java +517 -0
  186. data/src/java/arjdbc/util/NumberUtils.java +50 -0
  187. data/src/java/arjdbc/util/ObjectSupport.java +65 -0
  188. data/src/java/arjdbc/util/QuotingUtils.java +139 -0
  189. data/src/java/arjdbc/util/StringCache.java +60 -0
  190. data/src/java/arjdbc/util/StringHelper.java +155 -0
  191. metadata +288 -0
@@ -0,0 +1,189 @@
1
+ /*
2
+ * The MIT License
3
+ *
4
+ * Copyright 2014 Karol Bucek.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+ package arjdbc.postgresql;
25
+
26
+ import java.sql.ResultSet;
27
+ import java.sql.SQLException;
28
+
29
+ import org.jruby.Ruby;
30
+ import org.jruby.RubyModule;
31
+ import org.jruby.RubyString;
32
+ import org.jruby.anno.JRubyMethod;
33
+ import org.jruby.exceptions.RaiseException;
34
+ import org.jruby.runtime.ThreadContext;
35
+ import org.jruby.runtime.builtin.IRubyObject;
36
+ import org.jruby.util.ByteList;
37
+
38
+ import arjdbc.jdbc.WithResultSet;
39
+ import static arjdbc.jdbc.RubyJdbcConnection.debugMessage;
40
+ import static arjdbc.jdbc.RubyJdbcConnection.executeImpl;
41
+ import static arjdbc.jdbc.RubyJdbcConnection.executeQueryImpl;
42
+ import static arjdbc.util.QuotingUtils.quoteCharAndDecorateWith;
43
+ import static arjdbc.util.QuotingUtils.quoteCharWith;
44
+
45
+ /**
46
+ * ArJdbc::PostgreSQL
47
+ *
48
+ * @author kares
49
+ */
50
+ @org.jruby.anno.JRubyModule(name = "ArJdbc::PostgreSQL")
51
+ public class PostgreSQLModule {
52
+
53
+ public static RubyModule load(final RubyModule arJdbc) {
54
+ RubyModule postgreSQL = arJdbc.defineModuleUnder("PostgreSQL");
55
+ postgreSQL.defineAnnotatedMethods( PostgreSQLModule.class );
56
+ return postgreSQL;
57
+ }
58
+
59
+ public static RubyModule load(final Ruby runtime) {
60
+ return load( arjdbc.ArJdbcModule.get(runtime) );
61
+ }
62
+
63
+ @JRubyMethod(name = "quote_column_name", required = 1, frame = false)
64
+ public static IRubyObject quote_column_name(
65
+ final ThreadContext context,
66
+ final IRubyObject self,
67
+ final IRubyObject string) { // %("#{name.to_s.gsub("\"", "\"\"")}")
68
+ return quoteCharAndDecorateWith(context, string.asString(), '"', '"', (byte) '"', (byte) '"');
69
+ }
70
+
71
+ private static final ByteList BYTES_BACKSLASH = new ByteList(new byte[] { '\\' }, false);
72
+ private static final ByteList BYTES_ANDAND = new ByteList(new byte[] { '\\', '&', '\\', '&' }, false);
73
+
74
+ @JRubyMethod(name = "quote_string", required = 1, frame = false)
75
+ public static IRubyObject quote_string(
76
+ final ThreadContext context,
77
+ final IRubyObject self,
78
+ final IRubyObject string) {
79
+ final RubyString str = string.asString();
80
+ // quoted = string.gsub("'", "''")
81
+ RubyString quoted = quoteCharWith(context, str, '\'', '\'');
82
+
83
+ if ( ! standard_conforming_strings(context, self) ) { // minor branch
84
+ // quoted.gsub!(/\\/, '\&\&')
85
+ return quoted.callMethod(context, "gsub", new IRubyObject[] {
86
+ context.runtime.newString(BYTES_BACKSLASH),
87
+ context.runtime.newString(BYTES_ANDAND)
88
+ });
89
+ }
90
+ return quoted;
91
+ }
92
+
93
+ @JRubyMethod(name = "standard_conforming_strings?")
94
+ public static IRubyObject standard_conforming_strings_p(final ThreadContext context, final IRubyObject self) {
95
+ return context.runtime.newBoolean( standard_conforming_strings(context, self) );
96
+ }
97
+
98
+ private static boolean standard_conforming_strings(final ThreadContext context, final IRubyObject self) {
99
+ IRubyObject standard_conforming_strings =
100
+ self.getInstanceVariables().getInstanceVariable("@standard_conforming_strings");
101
+
102
+ if ( standard_conforming_strings == null ) {
103
+ synchronized (self) {
104
+ standard_conforming_strings = context.nil; // unsupported
105
+
106
+ final IRubyObject client_min_messages = self.callMethod(context, "client_min_messages");
107
+ final Ruby runtime = context.runtime;
108
+ try {
109
+ self.callMethod(context, "client_min_messages=", runtime.newString("panic"));
110
+ // NOTE: we no longer log this query as before ... with :
111
+ // select_one('SHOW standard_conforming_strings', 'SCHEMA')['standard_conforming_strings']
112
+ standard_conforming_strings = executeQueryImpl(context, self,
113
+ "SHOW standard_conforming_strings", 1, false, new WithResultSet<IRubyObject>() {
114
+ public IRubyObject call(final ResultSet resultSet) throws SQLException {
115
+ if ( resultSet != null && resultSet.next() ) {
116
+ final String result = resultSet.getString(1);
117
+ return runtime.newBoolean( "on".equals(result) );
118
+ }
119
+ return context.nil;
120
+ }
121
+ });
122
+ }
123
+ catch (SQLException e) { // unsupported
124
+ debugMessage(context.runtime, "standard conforming strings not supported: ", e);
125
+ }
126
+ catch (RaiseException e) { // unsupported
127
+ debugMessage(context.runtime, "standard conforming strings raised: ", e);
128
+ }
129
+ finally {
130
+ self.callMethod(context, "client_min_messages=", client_min_messages);
131
+ }
132
+
133
+ self.getInstanceVariables().setInstanceVariable("@standard_conforming_strings", standard_conforming_strings);
134
+ }
135
+ }
136
+
137
+ return standard_conforming_strings.isTrue();
138
+ }
139
+
140
+ @JRubyMethod(name = "standard_conforming_strings=")
141
+ public static IRubyObject set_standard_conforming_strings(final ThreadContext context, final IRubyObject self,
142
+ final IRubyObject enable) {
143
+ //
144
+ // client_min_messages = self.client_min_messages
145
+ // begin
146
+ // self.client_min_messages = 'panic'
147
+ // value = enable ? "on" : "off"
148
+ // execute("SET standard_conforming_strings = #{value}", 'SCHEMA')
149
+ // @standard_conforming_strings = ( value == "on" )
150
+ // rescue
151
+ // @standard_conforming_strings = :unsupported
152
+ // ensure
153
+ // self.client_min_messages = client_min_messages
154
+ // end
155
+ //
156
+ IRubyObject standard_conforming_strings = context.nil; // unsupported
157
+ final IRubyObject client_min_messages = self.callMethod(context, "client_min_messages");
158
+
159
+ try {
160
+ final Ruby runtime = context.runtime;
161
+ self.callMethod(context, "client_min_messages=", runtime.newString("panic"));
162
+
163
+ final String value;
164
+ if ( enable.isNil() || enable == runtime.getFalse() ) value = "off";
165
+ else value = "on";
166
+ // NOTE: we no longer log this query as before ... with :
167
+ // execute("SET standard_conforming_strings = #{value}", 'SCHEMA')
168
+ executeImpl(context, self, "SET standard_conforming_strings = " + value);
169
+
170
+ standard_conforming_strings = runtime.newBoolean( value == (Object) "on" );
171
+ }
172
+ catch (RaiseException e) {
173
+ // unsupported
174
+ }
175
+ finally {
176
+ self.callMethod(context, "client_min_messages=", client_min_messages);
177
+ }
178
+
179
+ return self.getInstanceVariables().setInstanceVariable("@standard_conforming_strings", standard_conforming_strings);
180
+ }
181
+
182
+ @JRubyMethod(name = "unescape_bytea", meta = true)
183
+ public static RubyString unescape_bytea(final ThreadContext context, final IRubyObject self, final IRubyObject escaped) {
184
+ final ByteList bytes = ((RubyString) escaped).getByteList();
185
+ final byte[] rawBytes = ByteaUtils.toBytes(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize());
186
+ return RubyString.newString(context.runtime, new ByteList(rawBytes, false));
187
+ }
188
+
189
+ }
@@ -0,0 +1,489 @@
1
+ /***** BEGIN LICENSE BLOCK *****
2
+ * Copyright (c) 2012-2015 Karol Bucek <self@kares.org>
3
+ * Copyright (c) 2006-2010 Nick Sieger <nick@nicksieger.com>
4
+ * Copyright (c) 2006-2007 Ola Bini <ola.bini@gmail.com>
5
+ * Copyright (c) 2008-2009 Thomas E Enebo <enebo@acm.org>
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining
8
+ * a copy of this software and associated documentation files (the
9
+ * "Software"), to deal in the Software without restriction, including
10
+ * without limitation the rights to use, copy, modify, merge, publish,
11
+ * distribute, sublicense, and/or sell copies of the Software, and to
12
+ * permit persons to whom the Software is furnished to do so, subject to
13
+ * the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be
16
+ * included in all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ ***** END LICENSE BLOCK *****/
26
+ package arjdbc.postgresql;
27
+
28
+ import java.io.ByteArrayInputStream;
29
+ import java.io.InputStream;
30
+ import java.lang.reflect.InvocationTargetException;
31
+ import java.sql.Array;
32
+ import java.sql.Connection;
33
+ import java.sql.PreparedStatement;
34
+ import java.sql.ResultSet;
35
+ import java.sql.SQLException;
36
+ import java.sql.Statement;
37
+ import java.sql.Types;
38
+
39
+ import org.jruby.Ruby;
40
+ import org.jruby.RubyArray;
41
+ import org.jruby.RubyBoolean;
42
+ import org.jruby.RubyClass;
43
+ import org.jruby.RubyFixnum;
44
+ import org.jruby.RubyFloat;
45
+ import org.jruby.RubyIO;
46
+ import org.jruby.RubyString;
47
+ import org.jruby.anno.JRubyMethod;
48
+ import org.jruby.runtime.ObjectAllocator;
49
+ import org.jruby.runtime.ThreadContext;
50
+ import org.jruby.runtime.builtin.IRubyObject;
51
+ import org.jruby.util.ByteList;
52
+ import org.jruby.util.SafePropertyAccessor;
53
+
54
+ import arjdbc.jdbc.DriverWrapper;
55
+ import arjdbc.util.DateTimeUtils;
56
+
57
+ /**
58
+ *
59
+ * @author enebo
60
+ */
61
+ @org.jruby.anno.JRubyClass(name = "ActiveRecord::ConnectionAdapters::PostgreSQLJdbcConnection")
62
+ public class PostgreSQLRubyJdbcConnection extends arjdbc.jdbc.RubyJdbcConnection {
63
+ private static final long serialVersionUID = 7235537759545717760L;
64
+
65
+ public PostgreSQLRubyJdbcConnection(Ruby runtime, RubyClass metaClass) {
66
+ super(runtime, metaClass);
67
+ }
68
+
69
+ public static RubyClass createPostgreSQLJdbcConnectionClass(Ruby runtime, RubyClass jdbcConnection) {
70
+ final RubyClass clazz = getConnectionAdapters(runtime).
71
+ defineClassUnder("PostgreSQLJdbcConnection", jdbcConnection, ALLOCATOR);
72
+ clazz.defineAnnotatedMethods(PostgreSQLRubyJdbcConnection.class);
73
+ return clazz;
74
+ }
75
+
76
+ public static RubyClass load(final Ruby runtime) {
77
+ RubyClass jdbcConnection = getJdbcConnection(runtime);
78
+ return createPostgreSQLJdbcConnectionClass(runtime, jdbcConnection);
79
+ }
80
+
81
+ protected static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
82
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
83
+ return new PostgreSQLRubyJdbcConnection(runtime, klass);
84
+ }
85
+ };
86
+
87
+ @Override
88
+ protected DriverWrapper newDriverWrapper(final ThreadContext context, final String driver) {
89
+ DriverWrapper driverWrapper = super.newDriverWrapper(context, driver);
90
+
91
+ final java.sql.Driver jdbcDriver = driverWrapper.getDriverInstance();
92
+ if ( jdbcDriver.getClass().getName().startsWith("org.postgresql.") ) {
93
+ try { // public static String getVersion()
94
+ final String version = (String) // "PostgreSQL 9.2 JDBC4 (build 1002)"
95
+ jdbcDriver.getClass().getMethod("getVersion").invoke(null);
96
+ if ( version != null && version.indexOf("JDBC3") >= 0 ) {
97
+ // config[:connection_alive_sql] ||= 'SELECT 1'
98
+ setConfigValueIfNotSet(context, "connection_alive_sql", context.runtime.newString("SELECT 1"));
99
+ }
100
+ }
101
+ catch (NoSuchMethodException e) { }
102
+ catch (SecurityException e) { }
103
+ catch (IllegalAccessException e) { }
104
+ catch (InvocationTargetException e) { }
105
+ }
106
+
107
+ return driverWrapper;
108
+ }
109
+
110
+ protected final IRubyObject beginTransaction(final ThreadContext context, final Connection connection,
111
+ final IRubyObject isolation) throws SQLException {
112
+ // NOTE: only reversed order - just to ~ match how Rails does it :
113
+ /* if ( connection.getAutoCommit() ) */ connection.setAutoCommit(false);
114
+ if ( isolation != null ) {
115
+ setTransactionIsolation(context, connection, isolation);
116
+ }
117
+ return context.nil;
118
+ }
119
+
120
+ final DriverImplementation driverImplementation = new PGDriverImplementation(); // currently no other supported
121
+
122
+ DriverImplementation driverImplementation() { return driverImplementation; }
123
+
124
+ // enables testing if the bug is fixed (please run our test-suite)
125
+ // using `rake test_postgresql JRUBY_OPTS="-J-Darjdbc.postgresql.generated_keys=true"`
126
+ protected static final boolean generatedKeys;
127
+ static {
128
+ String genKeys = SafePropertyAccessor.getProperty("arjdbc.postgresql.generated_keys");
129
+ if ( genKeys == null ) { // @deprecated system property name :
130
+ genKeys = SafePropertyAccessor.getProperty("arjdbc.postgresql.generated.keys");
131
+ }
132
+ generatedKeys = Boolean.parseBoolean(genKeys);
133
+ }
134
+
135
+ @Override
136
+ protected IRubyObject mapGeneratedKeys(
137
+ final Ruby runtime, final Connection connection,
138
+ final Statement statement, final Boolean singleResult)
139
+ throws SQLException {
140
+ // NOTE: PostgreSQL driver supports generated keys but does not work
141
+ // correctly for all cases e.g. for tables whene no keys are generated
142
+ // during an INSERT getGeneratedKeys return all inserted rows instead
143
+ // of an empty result set ... thus disabled until issue is resolved !
144
+ if ( ! generatedKeys ) return null; // not supported
145
+ // NOTE: generated-keys is implemented by the Postgre's JDBC driver by
146
+ // adding a "RETURNING" suffix after the executeUpdate passed query ...
147
+ return super.mapGeneratedKeys(runtime, connection, statement, singleResult);
148
+ }
149
+
150
+ // storesMixedCaseIdentifiers() return false;
151
+ // storesLowerCaseIdentifiers() return true;
152
+ // storesUpperCaseIdentifiers() return false;
153
+
154
+ @Override
155
+ protected final String caseConvertIdentifierForRails(final Connection connection, final String value)
156
+ throws SQLException {
157
+ return value;
158
+ }
159
+
160
+ @Override
161
+ protected final String caseConvertIdentifierForJdbc(final Connection connection, final String value)
162
+ throws SQLException {
163
+ return value;
164
+ }
165
+
166
+ @Override
167
+ protected final Connection newConnection() throws SQLException {
168
+ return driverImplementation().newConnection( getConnectionFactory().newConnection() );
169
+ }
170
+
171
+ @Override // due statement.setNull(index, Types.BLOB) not working :
172
+ // org.postgresql.util.PSQLException: ERROR: column "sample_binary" is of type bytea but expression is of type oid
173
+ protected void setBlobParameter(final ThreadContext context,
174
+ final Connection connection, final PreparedStatement statement,
175
+ final int index, final Object value,
176
+ final IRubyObject column, final int type) throws SQLException {
177
+ if ( value instanceof IRubyObject ) {
178
+ setBlobParameter(context, connection, statement, index, (IRubyObject) value, column, type);
179
+ }
180
+ else {
181
+ if ( value == null ) statement.setNull(index, Types.BINARY);
182
+ else {
183
+ statement.setBinaryStream(index, (InputStream) value);
184
+ }
185
+ }
186
+ }
187
+
188
+ @Override // due statement.setNull(index, Types.BLOB) not working :
189
+ // org.postgresql.util.PSQLException: ERROR: column "sample_binary" is of type bytea but expression is of type oid
190
+ protected void setBlobParameter(final ThreadContext context,
191
+ final Connection connection, final PreparedStatement statement,
192
+ final int index, final IRubyObject value,
193
+ final IRubyObject column, final int type) throws SQLException {
194
+ if ( value.isNil() ) {
195
+ statement.setNull(index, Types.BINARY);
196
+ }
197
+ else {
198
+ if ( value instanceof RubyIO ) { // IO/File
199
+ statement.setBinaryStream(index, ((RubyIO) value).getInStream());
200
+ }
201
+ else { // should be a RubyString
202
+ final ByteList blob = value.asString().getByteList();
203
+ statement.setBinaryStream(index,
204
+ new ByteArrayInputStream(blob.unsafeBytes(), blob.getBegin(), blob.getRealSize()),
205
+ blob.getRealSize() // length
206
+ );
207
+ }
208
+ }
209
+ }
210
+
211
+ @Override // to handle infinity timestamp values
212
+ protected void setTimestampParameter(final ThreadContext context,
213
+ final Connection connection, final PreparedStatement statement,
214
+ final int index, IRubyObject value,
215
+ final IRubyObject column, final int type) throws SQLException {
216
+
217
+ if ( ! driverImplementation().setTimestampParameter(context, connection, statement, index, value, column, type) ) {
218
+ super.setTimestampParameter(context, connection, statement, index, value, column, type);
219
+ }
220
+ }
221
+
222
+ @Override
223
+ protected void setStringParameter(final ThreadContext context,
224
+ final Connection connection, final PreparedStatement statement,
225
+ final int index, final IRubyObject value,
226
+ final IRubyObject column, final int type) throws SQLException {
227
+
228
+ if ( ! driverImplementation().setStringParameter(context, connection, statement, index, value, column, type) ) {
229
+ super.setStringParameter(context, connection, statement, index, value, column, type);
230
+ }
231
+ }
232
+
233
+ static int oid(final ThreadContext context, final IRubyObject column) {
234
+ // our column convention :
235
+ IRubyObject oid = column.getInstanceVariables().getInstanceVariable("@oid");
236
+ if ( oid == null || oid.isNil() ) { // only for user instantiated Column
237
+ throw new IllegalStateException("missing @oid for column: " + column.inspect());
238
+ }
239
+ return RubyFixnum.fix2int(oid);
240
+ }
241
+
242
+ @Override
243
+ protected void setObjectParameter(final ThreadContext context,
244
+ final Connection connection, final PreparedStatement statement,
245
+ final int index, Object value,
246
+ final IRubyObject column, final int type) throws SQLException {
247
+
248
+ if ( ! driverImplementation().setObjectParameter(context, connection, statement, index, value, column, type) ) {
249
+ super.setObjectParameter(context, connection, statement, index, value, column, type);
250
+ }
251
+ }
252
+
253
+ @Override
254
+ protected String resolveArrayBaseTypeName(final ThreadContext context,
255
+ final Object value, final IRubyObject column, final int type) {
256
+ String sqlType = column.callMethod(context, "sql_type").toString();
257
+ if ( sqlType.startsWith("character varying") ) return "text";
258
+ final int index = sqlType.indexOf('('); // e.g. "character varying(255)"
259
+ if ( index > 0 ) sqlType = sqlType.substring(0, index);
260
+ return sqlType;
261
+ }
262
+
263
+ //private static final int HSTORE_TYPE = 100000 + 1111;
264
+
265
+ @Override
266
+ protected int jdbcTypeFor(final ThreadContext context, final Ruby runtime,
267
+ final IRubyObject column, final Object value) throws SQLException {
268
+ // NOTE: likely wrong but native adapters handles this thus we should
269
+ // too - used from #table_exists? `binds << [ nil, schema ] if schema`
270
+ if ( column == null || column.isNil() ) return Types.VARCHAR; // assume type == :string
271
+ final int type = super.jdbcTypeFor(context, runtime, column, value);
272
+ /*
273
+ if ( type == Types.OTHER ) {
274
+ final IRubyObject columnType = column.callMethod(context, "type");
275
+ if ( "hstore" == (Object) columnType.asJavaString() ) {
276
+ return HSTORE_TYPE;
277
+ }
278
+ } */
279
+ return type;
280
+ }
281
+
282
+ /**
283
+ * Override jdbcToRuby type conversions to handle infinite timestamps.
284
+ * Handing timestamp off to ruby as string so adapter can perform type
285
+ * conversion to timestamp
286
+ */
287
+ @Override
288
+ protected IRubyObject jdbcToRuby(
289
+ final ThreadContext context, final Ruby runtime,
290
+ final int column, final int type, final ResultSet resultSet)
291
+ throws SQLException {
292
+ switch ( type ) {
293
+ case Types.BIT:
294
+ // we do get BIT for 't' 'f' as well as BIT strings e.g. "0110" :
295
+ final String bits = resultSet.getString(column);
296
+ if ( bits == null ) return context.nil;
297
+ if ( bits.length() > 1 ) {
298
+ return RubyString.newUnicodeString(runtime, bits);
299
+ }
300
+ return booleanToRuby(context, runtime, resultSet, column);
301
+ //case Types.JAVA_OBJECT: case Types.OTHER:
302
+ //return objectToRuby(runtime, resultSet, resultSet.getObject(column));
303
+ }
304
+ return super.jdbcToRuby(context, runtime, column, type, resultSet);
305
+ }
306
+
307
+ @Override
308
+ protected boolean useByteStrings() { return true; }
309
+
310
+ @Override
311
+ protected IRubyObject dateToRuby(final ThreadContext context,
312
+ final Ruby runtime, final ResultSet resultSet, final int column)
313
+ throws SQLException {
314
+
315
+ if ( rawDateTime != null && rawDateTime.booleanValue() ) {
316
+ final byte[] value = resultSet.getBytes(column);
317
+ if ( value == null ) {
318
+ if ( resultSet.wasNull() ) return context.nil;
319
+ return RubyString.newEmptyString(runtime); // ""
320
+ }
321
+ return RubyString.newString(runtime, new ByteList(value, false));
322
+ }
323
+
324
+ final String value = resultSet.getString(column);
325
+ if ( value == null ) return context.nil;
326
+
327
+ return DateTimeUtils.parseDate(context, value);
328
+ }
329
+
330
+ @Override
331
+ protected IRubyObject timeToRuby(final ThreadContext context,
332
+ final Ruby runtime, final ResultSet resultSet, final int column)
333
+ throws SQLException {
334
+ return timestampToRuby(context, runtime, resultSet, column);
335
+ }
336
+
337
+ @Override
338
+ protected IRubyObject timestampToRuby(final ThreadContext context,
339
+ final Ruby runtime, final ResultSet resultSet, final int column)
340
+ throws SQLException {
341
+
342
+ if ( rawDateTime != null && rawDateTime.booleanValue() ) {
343
+ final byte[] value = resultSet.getBytes(column);
344
+ if ( value == null ) {
345
+ if ( resultSet.wasNull() ) return context.nil;
346
+ return RubyString.newEmptyString(runtime); // ""
347
+ }
348
+ return RubyString.newString(runtime, new ByteList(value, false));
349
+ }
350
+
351
+ // NOTE: using Timestamp we would loose information such as BC :
352
+ // Timestamp: '0001-12-31 22:59:59.0' String: '0001-12-31 22:59:59 BC'
353
+
354
+ final String value = resultSet.getString(column);
355
+ if ( value == null ) return context.nil;
356
+
357
+ // string_to_time
358
+ final int len = value.length();
359
+ if ( (len == 8 || len == 9) && value.charAt(len - 1) == 'y' ) {
360
+ if ( value.charAt(0) == '-' ) { // 'infinity' / '-infinity'
361
+ return RubyFloat.newFloat(context.runtime, -RubyFloat.INFINITY);
362
+ }
363
+ return RubyFloat.newFloat(context.runtime, RubyFloat.INFINITY);
364
+ }
365
+ return DateTimeUtils.parseDateTime(context, value);
366
+ /*
367
+ final IRubyObject adapter = callMethod(context, "adapter"); // self.adapter
368
+ if ( usesType(runtime) ) {
369
+ return typeCastFromDatabase(context, adapter, runtime.newSymbol("timestamp"), strValue);
370
+ }
371
+
372
+ if ( adapter.isNil() ) return strValue; // NOTE: we warn on init_connection
373
+ return adapter.callMethod(context, "_string_to_timestamp", strValue);
374
+ */
375
+ }
376
+
377
+ @Override
378
+ protected IRubyObject arrayToRuby(final ThreadContext context,
379
+ final Ruby runtime, final ResultSet resultSet, final int column)
380
+ throws SQLException {
381
+ if ( rawArrayType == Boolean.TRUE ) { // pre AR 4.0 compatibility
382
+ return stringToRuby(context, runtime, resultSet, column);
383
+ }
384
+ // NOTE: avoid `finally { array.free(); }` on PostgreSQL due :
385
+ // java.sql.SQLFeatureNotSupportedException:
386
+ // Method org.postgresql.jdbc4.Jdbc4Array.free() is not yet implemented.
387
+ final Array value = resultSet.getArray(column);
388
+
389
+ if ( value == null /* || resultSet.wasNull() */ ) return context.nil;
390
+
391
+ final RubyArray array = RubyArray.newArray(runtime);
392
+
393
+ final ResultSet arrayResult = value.getResultSet(); // 1: index, 2: value
394
+ final int baseType = value.getBaseType();
395
+ while ( arrayResult.next() ) {
396
+ array.append( jdbcToRuby(context, runtime, 2, baseType, arrayResult) );
397
+ }
398
+ return array;
399
+ }
400
+
401
+ @Override
402
+ protected final IRubyObject objectToRuby(final ThreadContext context,
403
+ final Ruby runtime, final ResultSet resultSet, final int column)
404
+ throws SQLException {
405
+ return driverImplementation().objectToRuby(context, resultSet, column);
406
+ }
407
+
408
+ @Override
409
+ protected TableName extractTableName(
410
+ final Connection connection, String catalog, String schema,
411
+ final String tableName) throws IllegalArgumentException, SQLException {
412
+ // The postgres JDBC driver will default to searching every schema if no
413
+ // schema search path is given. Default to the 'public' schema instead:
414
+ if ( schema == null ) schema = "public";
415
+ return super.extractTableName(connection, catalog, schema, tableName);
416
+ }
417
+
418
+ static Boolean rawArrayType;
419
+ static {
420
+ final String arrayRaw = SafePropertyAccessor.getProperty("arjdbc.postgresql.array.raw");
421
+ if ( arrayRaw != null ) rawArrayType = Boolean.parseBoolean(arrayRaw);
422
+ }
423
+
424
+ @JRubyMethod(name = "raw_array_type?", meta = true)
425
+ public static IRubyObject useRawArrayType(final ThreadContext context, final IRubyObject self) {
426
+ if ( rawArrayType == null ) return context.nil;
427
+ return context.runtime.newBoolean(rawArrayType);
428
+ }
429
+
430
+ @JRubyMethod(name = "raw_array_type=", meta = true)
431
+ public static IRubyObject setRawArrayType(final IRubyObject self, final IRubyObject value) {
432
+ if ( value instanceof RubyBoolean ) {
433
+ rawArrayType = ((RubyBoolean) value).isTrue() ? Boolean.TRUE : Boolean.FALSE;
434
+ }
435
+ else {
436
+ rawArrayType = value.isNil() ? null : Boolean.TRUE;
437
+ }
438
+ return value;
439
+ }
440
+
441
+ static Boolean rawHstoreType;
442
+ static {
443
+ final String hstoreRaw = SafePropertyAccessor.getProperty("arjdbc.postgresql.hstore.raw");
444
+ if ( hstoreRaw != null ) rawHstoreType = Boolean.parseBoolean(hstoreRaw);
445
+ }
446
+
447
+ @JRubyMethod(name = "raw_hstore_type?", meta = true)
448
+ public static IRubyObject useRawHstoreType(final ThreadContext context, final IRubyObject self) {
449
+ if ( rawHstoreType == null ) return context.nil;
450
+ return context.runtime.newBoolean(rawHstoreType);
451
+ }
452
+
453
+ @JRubyMethod(name = "raw_hstore_type=", meta = true)
454
+ public static IRubyObject setRawHstoreType(final IRubyObject self, final IRubyObject value) {
455
+ if ( value instanceof RubyBoolean ) {
456
+ rawHstoreType = ((RubyBoolean) value).isTrue() ? Boolean.TRUE : Boolean.FALSE;
457
+ }
458
+ else {
459
+ rawHstoreType = value.isNil() ? null : Boolean.TRUE;
460
+ }
461
+ return value;
462
+ }
463
+
464
+ // whether to use "raw" interval values off by default - due native adapter compatibilty :
465
+ // RAW values :
466
+ // - 2 years 0 mons 0 days 0 hours 3 mins 0.00 secs
467
+ // - -1 years 0 mons -2 days 0 hours 0 mins 0.00 secs
468
+ // Rails style :
469
+ // - 2 years 00:03:00
470
+ // - -1 years -2 days
471
+ static boolean rawIntervalType = Boolean.getBoolean("arjdbc.postgresql.iterval.raw");
472
+
473
+ @JRubyMethod(name = "raw_interval_type?", meta = true)
474
+ public static IRubyObject useRawIntervalType(final ThreadContext context, final IRubyObject self) {
475
+ return context.runtime.newBoolean(rawIntervalType);
476
+ }
477
+
478
+ @JRubyMethod(name = "raw_interval_type=", meta = true)
479
+ public static IRubyObject setRawIntervalType(final IRubyObject self, final IRubyObject value) {
480
+ if ( value instanceof RubyBoolean ) {
481
+ rawIntervalType = ((RubyBoolean) value).isTrue();
482
+ }
483
+ else {
484
+ rawIntervalType = ! value.isNil();
485
+ }
486
+ return value;
487
+ }
488
+
489
+ }