1# 2# Copyright (c) 2012-2018 JEP AUTHORS. 3# 4# This file is licensed under the the zlib/libpng License. 5# 6# This software is provided 'as-is', without any express or implied 7# warranty. In no event will the authors be held liable for any 8# damages arising from the use of this software. 9# 10# Permission is granted to anyone to use this software for any 11# purpose, including commercial applications, and to alter it and 12# redistribute it freely, subject to the following restrictions: 13# 14# 1. The origin of this software must not be misrepresented; you 15# must not claim that you wrote the original software. If you use 16# this software in a product, an acknowledgment in the product 17# documentation would be appreciated but is not required. 18# 19# 2. Altered source versions must be plainly marked as such, and 20# must not be misrepresented as being the original software. 21# 22# 3. This notice may not be removed or altered from any source 23# distribution. 24# 25 26from decimal import Decimal 27import logging 28import java.sql 29import datetime 30import time 31 32 33log = logging.getLogger('java.sql') 34 35apilevel = '2.0' 36paramstyle = 'qmark' 37threadsafety = 2 38 39 40class Warning(Exception): 41 """Exception raised for important warnings like data 42 truncations while inserting, etc. It must be a subclass of 43 the Python StandardError (defined in the module 44 exceptions).""" 45 pass 46 47 48class Error(Exception): 49 """Exception that is the base class of all other error 50 exceptions. You can use this to catch all errors with one 51 single 'except' statement. Warnings are not considered 52 errors and thus should not use this class as base. It must 53 be a subclass of the Python StandardError (defined in the 54 module exceptions). 55 """ 56 pass 57 58 59class InterfaceError(Error): 60 """Exception raised for errors that are related to the 61 database interface rather than the database itself. It 62 must be a subclass of Error.""" 63 pass 64 65 66class DatabaseError(Error): 67 """Exception raised for errors that are related to the 68 database. It must be a subclass of Error.""" 69 pass 70 71 72class DataError(DatabaseError): 73 """Exception raised for errors that are due to problems with 74 the processed data like division by zero, numeric value 75 out of range, etc. It must be a subclass of DatabaseError.""" 76 pass 77 78 79class OperationalError(DatabaseError): 80 """Exception raised for errors that are related to the 81 database's operation and not necessarily under the control 82 of the programmer, e.g. an unexpected disconnect occurs, 83 the data source name is not found, a transaction could not 84 be processed, a memory allocation error occurred during 85 processing, etc. It must be a subclass of DatabaseError.""" 86 pass 87 88 89class IntegrityError(DatabaseError): 90 """Exception raised when the relational integrity of the 91 database is affected, e.g. a foreign key check fails. It 92 must be a subclass of DatabaseError.""" 93 pass 94 95 96class InternalError(DatabaseError): 97 """Exception raised when the database encounters an internal 98 error, e.g. the cursor is not valid anymore, the 99 transaction is out of sync, etc. It must be a subclass of 100 DatabaseError.""" 101 pass 102 103 104class ProgrammingError(DatabaseError): 105 """Exception raised for programming errors, e.g. table not 106 found or already exists, syntax error in the SQL 107 statement, wrong number of parameters specified, etc. It 108 must be a subclass of DatabaseError.""" 109 pass 110 111 112class NotSupportedError(DatabaseError): 113 """"Exception raised in case a method or database API was used 114 which is not supported by the database, e.g. requesting a 115 .rollback() on a connection that does not support 116 transaction or has transactions turned off. It must be a 117 subclass of DatabaseError.""" 118 pass 119 120 121def Date(year, month, day): 122 return java.sql.Date(int(time.mktime(datetime.date(year, month, day).timetuple())) * 1000) 123 124 125def Time(hour, minute, second): 126 return java.sql.Time(hour, minute, second) 127 128 129def Timestamp(year, month, day, hour, minute, second): 130 return java.sql.Timestamp( 131 int(time.mktime(datetime.datetime(year, month, day, hour, minute, second).timetuple())) * 1000) 132 133 134def DateFromTicks(ticks): 135 return Date(*time.localtime(ticks)[:3]) 136 137 138def TimeFromTicks(ticks): 139 return Time(*time.localtime(ticks)[3:6]) 140 141 142def TimestampFromTicks(ticks): 143 return Timestamp(*time.localtime(ticks)[:6]) 144 145 146def connect(url, user=None, password=None, timeout=0): 147 """ 148 Connect to a JDBC data source using java.sql.DriverManager. 149 Returns a java.sql.Connection instance. 150 151 url - JDBC connection url. See your driver's documentation for this value 152 user - connection username 153 password - optional password 154 timeout - time in seconds to wait for a connection. 155 156 Raises ``Error`` for failures. 157 """ 158 try: 159 java.sql.DriverManager.setLoginTimeout(timeout) 160 return JDBCConnection(java.sql.DriverManager.getConnection(url, user, password)) 161 except Exception as e: 162 raise Error(e.args) 163 164 165class JDBCConnection(object): 166 167 def __init__(self, conn): 168 super(JDBCConnection, self).__init__() 169 self.conn = conn 170 171 def close(self): 172 try: 173 self.conn.close() 174 except Exception as e: 175 raise DatabaseError(e.args) 176 177 def commit(self): 178 try: 179 self.conn.commit() 180 except Exception as e: 181 raise DatabaseError(e.args) 182 183 def rollback(self): 184 try: 185 self.conn.rollback() 186 except Exception as e: 187 raise DatabaseError(e.args) 188 189 def cursor(self): 190 return JDBCCursor(self) 191 192 193class JDBCCursor(object): 194 195 def __init__(self, jconn): 196 super(JDBCCursor, self).__init__() 197 self.connection = jconn 198 self.statement = None # jdbc Statement 199 self.rs = None # jdbc ResultSet 200 self.meta_data = None # jdbc ResultSetMetaData 201 self.columns = None # column count 202 203 self.description = () 204 self.rowcount = None 205 self.arraysize = 1 206 207 def _prepare(self, sql): 208 self.rs = None 209 self.meta_data = None 210 self.columns = None 211 self.rowcount = None 212 213 self.statement = self.connection.conn.prepareStatement(sql) 214 215 def _set_parameters(self, args): 216 for index, arg in enumerate(args): 217 index += 1 218 219 if isinstance(arg, int): 220 self.statement.setLong(index, arg) 221 elif isinstance(arg, float): 222 self.statement.setDouble(index, arg) 223 elif isinstance(arg, str): 224 self.statement.setString(index, arg) 225 else: 226 self.statement.setObject(index, arg) 227 228 def execute(self, operation, *args): 229 try: 230 if log.isEnabledFor(logging.DEBUG): 231 log.debug('%s -- %s', operation.strip(), str(args)) 232 233 self._prepare(operation) 234 self._set_parameters(args) 235 236 is_update = not self.statement.execute() 237 if is_update: 238 self.rowcount = self.statement.getUpdateCount() 239 else: 240 self.rs = self.statement.getResultSet() 241 self.meta_data = self.rs.getMetaData() 242 self.columns = self.meta_data.getColumnCount() 243 244 desc = [] 245 for col in range(1, self.columns + 1): 246 desc.append(( 247 self.meta_data.getColumnName(col), 248 self.meta_data.getColumnType(col), 249 self.meta_data.getColumnDisplaySize(col), 250 self.meta_data.getColumnDisplaySize(col), 251 self.meta_data.getPrecision(col), 252 self.meta_data.getScale(col), 253 bool(self.meta_data.isNullable(col)), 254 )) 255 self.description = tuple(desc) 256 257 except Exception as e: 258 raise DatabaseError(str(e.args[0])) 259 260 def executemany(self, operation, seq_of_parameters): 261 if log.isEnabledFor(logging.DEBUG): 262 log.debug('executemany: %s', operation.strip()) 263 264 self._prepare(operation) 265 for args in seq_of_parameters: 266 self._set_parameters(args) 267 self.statement.addBatch() 268 269 self.statement.executeBatch() 270 271 def fetchone(self): 272 if not self.rs.next(): 273 return None 274 275 def map_type(col): 276 try: 277 sql_type = self.description[col - 1][1] 278 279 # see http://docs.oracle.com/javase/6/docs/api/constant-values.html#java.sql.Types.VARCHAR 280 if self.rs.getString(col) is None: 281 return None 282 if sql_type in (-5, -7, 4, 5, -6): 283 return self.rs.getLong(col) 284 if sql_type in (8, 6, 7): 285 return self.rs.getDouble(col) 286 if sql_type in (-1, 1, 0, 12, -16, -9, -15): 287 return self.rs.getString(col) 288 if sql_type in (16,): 289 return self.rs.getBoolean(col) 290 if sql_type in (2, 3,): 291 return Decimal(self.rs.getString(col)) 292 if sql_type in (91,): 293 return self.rs.getDate(col) 294 if sql_type in (92,): 295 return self.rs.getTime(col) 296 if sql_type in (93,): 297 return self.rs.getTimestamp(col) 298 299 return self.rs.getObject(col) 300 except Exception as e: 301 log.exception("Failed to map ResultSet type") 302 raise DatabaseError(e.args) 303 304 #public static final int BLOB 2004 305 #public static final int CLOB 2005 306 #public static final int DATALINK 70 307 #public static final int DISTINCT 2001 308 #public static final int JAVA_OBJECT 2000 309 #public static final int LONGVARBINARY -4 310 #public static final int NCLOB 2011 311 #public static final int OTHER 1111 312 #public static final int REF 2006 313 #public static final int ROWID -8 314 #public static final int SQLXML 2009 315 #public static final int STRUCT 2002 316 #public static final int VARBINARY -3 317 318 return tuple(map(map_type, range(1, self.columns + 1))) 319 320 def fetchall(self): 321 return list(iter(self.fetchone, None)) 322 323 def close(self): 324 try: 325 self.rs.close() 326 except Exception as e: 327 log.exception('Ignored error') 328 try: 329 self.statement.close() 330 except Exception as e: 331 log.exception('Ignored error') 332 333 def nextset(self): 334 raise ProgrammingError("Not Implemented") 335