PageRenderTime 49ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/IBM_DB/ibm_db_sa/ibm_db_sa/ibm_db_sa.py

http://ibm-db.googlecode.com/
Python | 784 lines | 635 code | 58 blank | 91 comment | 48 complexity | 79334b0c11b2b035b647a00828fd342b MD5 | raw file
Possible License(s): Apache-2.0
  1. # +--------------------------------------------------------------------------+
  2. # | Licensed Materials - Property of IBM |
  3. # | |
  4. # | (C) Copyright IBM Corporation 2008. |
  5. # +--------------------------------------------------------------------------+
  6. # | This module complies with SQLAlchemy 0.4 and is |
  7. # | Licensed under the Apache License, Version 2.0 (the "License"); |
  8. # | you may not use this file except in compliance with the License. |
  9. # | You may obtain a copy of the License at |
  10. # | http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable |
  11. # | law or agreed to in writing, software distributed under the License is |
  12. # | distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
  13. # | KIND, either express or implied. See the License for the specific |
  14. # | language governing permissions and limitations under the License. |
  15. # +--------------------------------------------------------------------------+
  16. # | Authors: Alex Pitigoi, Abhigyan Agrawal |
  17. # | Version: 0.1.6 |
  18. # +--------------------------------------------------------------------------+
  19. """
  20. This module implements the SQLAlchemy version 0.4 for IBM DB2 Data Servers.
  21. """
  22. from sqlalchemy import sql, engine, schema, exceptions, logging
  23. from sqlalchemy import types as sa_types
  24. from sqlalchemy.engine import base, default, url
  25. from sqlalchemy.sql import compiler
  26. import pickle, os.path, re, datetime, pkg_resources
  27. # Load IBM_DB reserved words associated with supported IBM Data Servers (DB2)
  28. # ibm_db_reserved is a pickle file expected to share path with current file
  29. RESERVED_WORDS = pickle.load(
  30. pkg_resources.resource_stream('ibm_db_sa', 'ibm_db_reserved')
  31. )
  32. # Override module sqlalchemy.types
  33. class IBM_DBBinary(sa_types.Binary):
  34. def get_col_spec(self):
  35. if self.length is None:
  36. return "BLOB(1M)"
  37. else:
  38. return "BLOB(%s)" % self.length
  39. class IBM_DBString(sa_types.String):
  40. def get_col_spec(self):
  41. if self.length is None:
  42. return "LONG VARCHAR"
  43. else:
  44. return "VARCHAR(%s)" % self.length
  45. class IBM_DBBoolean(sa_types.Boolean):
  46. def get_col_spec(self):
  47. return "SMALLINT"
  48. def result_processor(self, dialect):
  49. def process(value):
  50. if value is None:
  51. return None
  52. if value == False:
  53. return 0
  54. elif value == True:
  55. return 1
  56. return process
  57. def bind_processor(self, dialect):
  58. def process(value):
  59. if value is None:
  60. return None
  61. if value == False:
  62. return '0'
  63. elif value == True:
  64. return '1'
  65. return process
  66. class IBM_DBInteger(sa_types.Integer):
  67. def get_col_spec(self):
  68. return "INTEGER"
  69. class IBM_DBNumeric(sa_types.Numeric):
  70. def get_col_spec(self):
  71. if not self.precision:
  72. return "DECIMAL(31,0)"
  73. else:
  74. return "DECIMAL(%(precision)s, %(length)s)" % {'precision': self.precision, 'length' : self.length}
  75. class IBM_DBDateTime(sa_types.DateTime):
  76. def get_col_spec(self):
  77. return "TIMESTAMP"
  78. def result_processor(self, dialect):
  79. def process(value):
  80. if value is None:
  81. return None
  82. if isinstance(value, datetime.datetime):
  83. value = datetime.datetime( value.year, value.month, value.day,
  84. value.hour, value.minute, value.second, value.microsecond)
  85. elif isinstance(value, datetime.time):
  86. value = datetime.datetime( value.year, value.month, value.day, 0, 0, 0, 0)
  87. return value
  88. return process
  89. def bind_processor(self, dialect):
  90. def process(value):
  91. if value is None:
  92. return None
  93. if isinstance(value, datetime.datetime):
  94. value = datetime.datetime( value.year, value.month, value.day,
  95. value.hour, value.minute, value.second, value.microsecond)
  96. elif isinstance(value, datetime.date):
  97. value = datetime.datetime( value.year, value.month, value.day, 0, 0, 0, 0)
  98. return str(value)
  99. return process
  100. class IBM_DBDate(sa_types.Date):
  101. def get_col_spec(self):
  102. return "DATE"
  103. def result_processor(self, dialect):
  104. def process(value):
  105. if value is None:
  106. return None
  107. if isinstance(value, datetime.datetime):
  108. value = datetime.date( value.year, value.month, value.day)
  109. elif isinstance(value, datetime.date):
  110. value = datetime.date( value.year, value.month, value.day)
  111. return value
  112. return process
  113. def bind_processor(self, dialect):
  114. def process(value):
  115. if value is None:
  116. return None
  117. if isinstance(value, datetime.datetime):
  118. value = datetime.date( value.year, value.month, value.day)
  119. elif isinstance(value, datetime.time):
  120. value = datetime.date( value.year, value.month, value.day)
  121. return str(value)
  122. return process
  123. class IBM_DBTime(sa_types.Time):
  124. def get_col_spec(self):
  125. return 'TIME'
  126. class IBM_DBTimeStamp(sa_types.TIMESTAMP):
  127. def get_col_spec(self):
  128. return 'TIMESTAMP'
  129. class IBM_DBDATETIME(sa_types.DATETIME):
  130. def get_col_spec(self):
  131. return 'TIMESTAMP'
  132. class IBM_DBSmallInteger(sa_types.SmallInteger):
  133. def get_col_spec(self):
  134. return 'SMALLINT'
  135. class IBM_DBFloat(sa_types.Float):
  136. def get_col_spec(self):
  137. return 'REAL'
  138. class IBM_DBFLOAT(sa_types.FLOAT):
  139. def get_col_spec(self):
  140. return 'REAL'
  141. class IBM_DBTEXT(sa_types.TEXT):
  142. def get_col_spec(self):
  143. if self.length is None:
  144. return 'LONG VARCHAR'
  145. else:
  146. return 'VARCHAR(%s)' % self.length
  147. class IBM_DBDecimal(sa_types.DECIMAL):
  148. def get_col_spec(self):
  149. if not self.precision:
  150. return 'DECIMAL(31,0)'
  151. else:
  152. return 'DECIMAL(%(precision)s, %(length)s)' % {'precision': self.precision, 'length' : self.length}
  153. class IBM_DBINT(sa_types.INT):
  154. def get_col_spec(self):
  155. return 'INT'
  156. class IBM_DBCLOB(sa_types.CLOB):
  157. def get_col_spec(self):
  158. return 'CLOB'
  159. class IBM_DBVARCHAR(sa_types.VARCHAR):
  160. def get_col_spec(self):
  161. if self.length is None:
  162. return 'LONG VARCHAR'
  163. else:
  164. return 'VARCHAR(%s)' % self.length
  165. class IBM_DBChar(sa_types.CHAR):
  166. def get_col_spec(self):
  167. if self.length is None:
  168. return 'CHAR'
  169. else:
  170. return 'CHAR(%s)' % self.length
  171. class IBM_DBBLOB(sa_types.BLOB):
  172. def get_col_spec(self):
  173. if self.length is None:
  174. return 'BLOB(1M)'
  175. else:
  176. return 'BLOB(%s)' % self.length
  177. class IBM_DBBOOLEAN(sa_types.BOOLEAN):
  178. def get_col_spec(self):
  179. return 'SMALLINT'
  180. class IBM_DBDouble(sa_types.Float):
  181. def get_col_spec(self):
  182. if self.length is None:
  183. return 'DOUBLE(15)'
  184. else:
  185. return 'DOUBLE(%(precision)s)' % self.precision
  186. class IBM_DBBigInteger(sa_types.TypeEngine):
  187. def get_col_spec(self):
  188. return 'BIGINT'
  189. class IBM_DBXML(sa_types.TypeEngine):
  190. def get_col_spec(self):
  191. return 'XML'
  192. # Module level dictionary maps standard SQLAlchemy types to IBM_DB data types.
  193. # The dictionary uses the SQLAlchemy data types as key, and maps an IBM_DB type as its value
  194. colspecs = {
  195. sa_types.Binary : IBM_DBBinary,
  196. sa_types.String : IBM_DBString,
  197. sa_types.Boolean : IBM_DBBoolean,
  198. sa_types.Integer : IBM_DBInteger,
  199. sa_types.Numeric : IBM_DBNumeric,
  200. sa_types.DateTime : IBM_DBDateTime,
  201. sa_types.Date : IBM_DBDate,
  202. sa_types.Time : IBM_DBTime,
  203. sa_types.SmallInteger : IBM_DBSmallInteger,
  204. sa_types.Float : IBM_DBFloat,
  205. sa_types.FLOAT : IBM_DBFloat,
  206. sa_types.TEXT : IBM_DBTEXT,
  207. sa_types.DECIMAL : IBM_DBDecimal,
  208. sa_types.INT : IBM_DBINT,
  209. sa_types.TIMESTAMP : IBM_DBTimeStamp,
  210. sa_types.DATETIME : IBM_DBDATETIME,
  211. sa_types.CLOB : IBM_DBCLOB,
  212. sa_types.VARCHAR : IBM_DBVARCHAR,
  213. sa_types.CHAR : IBM_DBChar,
  214. sa_types.BLOB : IBM_DBBLOB,
  215. sa_types.BOOLEAN : IBM_DBBOOLEAN
  216. }
  217. # Module level dictionary which maps the data type name returned by a database
  218. # to the IBM_DB type class allowing the correct type classes to be created
  219. # based on the information_schema. Any database type that is supported by the
  220. # IBM_DB shall be mapped to an equivalent data type.
  221. ischema_names = {
  222. 'BLOB' : IBM_DBBinary,
  223. 'CHAR' : IBM_DBChar,
  224. 'CLOB' : IBM_DBCLOB,
  225. 'DATE' : IBM_DBDate,
  226. 'DATETIME' : IBM_DBDateTime,
  227. 'INTEGER' : IBM_DBInteger,
  228. 'SMALLINT' : IBM_DBSmallInteger,
  229. 'BIGINT' : IBM_DBBigInteger,
  230. 'DECIMAL' : IBM_DBDecimal,
  231. 'REAL' : IBM_DBFloat,
  232. 'DOUBLE' : IBM_DBDouble,
  233. 'TIME' : IBM_DBTime,
  234. 'TIMESTAMP' : IBM_DBTimeStamp,
  235. 'VARCHAR' : IBM_DBString,
  236. 'LONG VARCHAR' : IBM_DBTEXT,
  237. 'XML' : IBM_DBXML
  238. }
  239. # Define the behavior of a specific database and DBAPI combination.
  240. # Its main responsibility is to act as interface between SQLAlchemy and the DBAPI driver.
  241. # Aspects that are specific to the data server are defined in this class including,
  242. # but not limited to, metadata definition, SQL-syntax for use in SQL query generation,
  243. # execution, and result-set handling.
  244. class IBM_DBDialect(default.DefaultDialect):
  245. positional = True
  246. encoding = 'utf-8'
  247. max_identifier_length = 128
  248. supports_alter = True
  249. supports_sane_rowcount = True
  250. supports_sane_multi_rowcount = True
  251. preexecute_sequences = False
  252. def __init__(self, **kwargs):
  253. """ Inputs: Supports any number of keyword arguments.
  254. Attributes not set by default or not set by the dialect module level
  255. class should be set here.
  256. """
  257. default.DefaultDialect.__init__(self, **kwargs)
  258. """String constant for parameter marker formatting expected.
  259. Possible values {'qmark', 'numeric', 'named', 'format', 'pyformat'}
  260. associated with { name=?, name=:1, :name, %s, %(name)s }
  261. """
  262. self.paramstyle = IBM_DBDialect.dbapi().paramstyle
  263. # Returns the underlying DBAPI driver module for access to class attributes/variables
  264. def dbapi(cls):
  265. """ Returns: the underlying DBAPI driver module
  266. """
  267. import ibm_db_dbi as module
  268. return module
  269. dbapi = classmethod(dbapi)
  270. # Retrieves the IBM Data Server version for a given connection object
  271. def server_version_info(self, connection):
  272. """ Inputs: - sqlalchemy.engine.base.Connection object has a <connection> reference
  273. to sqlalchemy.pool._ConnectionFairy which has a <connection> reference
  274. to sqlalchemy.databases.ibm_db_dbi.Connection, the actual DBAPI
  275. driver connection handler:
  276. sa_connection = connection
  277. sa_conn_fairy = sa_connection.connection
  278. ibm_db_dbi_conn = sa_conn_fairy.connection
  279. Returns: Tuple, representing the data server version.
  280. """
  281. version_info = connection.connection.connection.server_info()
  282. return version_info
  283. # Build DB-API compatible connection arguments.
  284. def create_connect_args(self, url):
  285. """ Inputs: sqlalchemy.engine.url object (attributes parsed from a RFC-1738-style string using
  286. module-level make_url() function - driver://username:password@host:port/database or
  287. driver:///?<attrib_1_name>=<attrib_1_value>;<attrib_2_name>=<attrib_2_value>)
  288. Returns: tuple consisting of a *args/**kwargs suitable to send directly to the dbapi connect function.
  289. DBAPI.connect(dsn, user='', password='', host='', database='', conn_options=None)
  290. DSN: 'DRIVER={IBM DB2 ODBC DRIVER};DATABASE=db_name;HOSTNAME=host_addr;
  291. PORT=50000;PROTOCOL=TCPIP;UID=user_id;PWD=secret'
  292. """
  293. conn_args = url.translate_connect_args()
  294. # DSN support through CLI configuration (../cfg/db2cli.ini), while 2 connection
  295. # attributes are mandatory: database alias and UID (in support to current schema),
  296. # all the other connection attributes (protocol, hostname, servicename) are provided
  297. # through db2cli.ini database catalog entry.
  298. # Example 1: ibm_db_sa:///<database_alias>?UID=db2inst1 or
  299. # Example 2: ibm_db_sa:///?DSN=<database_alias>;UID=db2inst1
  300. if str(url).find('///') != -1:
  301. dsn, uid, pwd = '', '', ''
  302. if 'database' in conn_args and conn_args['database'] is not None:
  303. dsn = conn_args['database']
  304. else:
  305. if 'DSN' in url.query and url.query['DSN'] is not None:
  306. dsn = url.query['DSN']
  307. if 'UID' in url.query and url.query['UID'] is not None:
  308. uid = url.query['UID']
  309. if 'PWD' in url.query and url.query['PWD'] is not None:
  310. pwd = url.query['PWD']
  311. return ((dsn, uid, pwd,'',''), {})
  312. else:
  313. # Full URL string support for connection to remote data servers
  314. dsn_param = ['DRIVER={IBM DB2 ODBC DRIVER}']
  315. dsn_param.append( 'DATABASE=%s' % conn_args['database'] )
  316. dsn_param.append( 'HOSTNAME=%s' % conn_args['host'] )
  317. dsn_param.append( 'PORT=%s' % conn_args['port'] )
  318. dsn_param.append( 'PROTOCOL=TCPIP' )
  319. dsn_param.append( 'UID=%s' % conn_args['username'] )
  320. dsn_param.append( 'PWD=%s' % conn_args['password'] )
  321. dsn = ';'.join(dsn_param)
  322. dsn += ';'
  323. dialect.logger.debug("\n *** IBM_DBDialect::create_connect_args: " + str(dsn))
  324. return ((dsn, conn_args['username'],'','',''), {})
  325. # Builds an execution context
  326. def create_execution_context(self,
  327. connection,
  328. compiled = None,
  329. compiled_parameters = None,
  330. statement = None,
  331. parameters = None):
  332. """ Inputs: Use the supplied parameters to construct a new ExecutionContext.
  333. The parameters should be passed on directly to the ExectionContext
  334. constructor, which knows how to use them to build the execution context.
  335. Connection object which is a proxy object to a DBAPI driver connection
  336. Returns: ExecutionContext object
  337. """
  338. dialect.logger.debug("\n *** IBM_DBDialect::create_execution_context: " + str(statement))
  339. return IBM_DBExecutionContext( self, connection, compiled, statement, parameters )
  340. # Retrieves current schema for the specified connection object
  341. def get_default_schema_name(self, connection):
  342. """ Inputs: - sqlalchemy.engine.base.Connection object has a <connection> reference
  343. to sqlalchemy.pool._ConnectionFairy which has a <connection> reference
  344. to sqlalchemy.databases.ibm_db_dbi.Connection, the actual DBAPI
  345. driver connection handler
  346. Returns: representing the current schema.
  347. """
  348. schema_name = connection.connection.connection.get_current_schema()
  349. return schema_name
  350. # Verifies if a specific table exists for a given schema
  351. def has_table(self, connection, table_name, schema=None):
  352. """ Inputs: - sqlalchemy.engine.base.Connection object has a <connection> reference
  353. to sqlalchemy.pool._ConnectionFairy which has a <connection> reference
  354. to sqlalchemy.databases.ibm_db_dbi.Connection, the actual DBAPI
  355. driver connection handler
  356. - table_name string
  357. - schema string, if not using the default schema
  358. Returns: True, if table exsits, False otherwise.
  359. """
  360. if schema is None:
  361. schema = self.get_default_schema_name(connection)
  362. table = connection.connection.connection.tables(schema, table_name)
  363. has_it = table is not None and \
  364. len(table) is not 0 \
  365. and table[0]['TABLE_NAME'] == table_name.upper()
  366. dialect.logger.debug("\n *** IBM_DBDialect::has_table( "+str(table_name)+', '+str(schema)+' ) = '+str(has_it))
  367. return has_it
  368. # Retrieves a list of table names for a given schema
  369. def table_names(self, connection, schema = None):
  370. """ Inputs: - sqlalchemy.engine.base.Connection object has a <connection> reference
  371. to sqlalchemy.pool._ConnectionFairy which has a <connection> reference
  372. to sqlalchemy.databases.ibm_db_dbi.Connection, the actual DBAPI
  373. driver connection handler
  374. - schema string, if not using the default schema
  375. Returns: List of strings representing table names
  376. """
  377. if schema is None:
  378. schema = self.get_default_schema_name(connection)
  379. names = []
  380. tables = connection.connection.connection.tables(schema)
  381. for table in tables:
  382. names.append(table['TABLE_NAME'].lower())
  383. dialect.logger.debug("\n *** IBM_DBDialect::table_names: " + str(names))
  384. return names
  385. # Retrieves a list of index names for a given schema
  386. def index_names(self, connection, table_name, schema = None):
  387. dialect.logger.debug("\n *** IBM_DBDialect::index_names( "+str(table_name)+', '+str(schema)+' )')
  388. """ Inputs: - sqlalchemy.engine.base.Connection object has a <connection> reference
  389. to sqlalchemy.pool._ConnectionFairy which has a <connection> reference
  390. to sqlalchemy.databases.ibm_db_dbi.Connection, the actual DBAPI
  391. driver connection handler
  392. - table_name string
  393. - schema string, if not using the default schema
  394. Returns: List of strings representing table names
  395. """
  396. if schema is None:
  397. schema = self.get_default_schema_name(connection)
  398. names = []
  399. index = connection.connection.connection.index(schema)
  400. return index
  401. # Loads table description (columns and properties) from the database for a given table object
  402. def reflecttable(self, connection, table, include_columns = None):
  403. """ Inputs: - sqlalchemy.engine.base.Connection object has a <connection> reference
  404. to sqlalchemy.pool._ConnectionFairy which has a <connection> reference
  405. to sqlalchemy.databases.ibm_db_dbi.Connection, the actual DBAPI
  406. driver connection handler
  407. - table object
  408. - include_columns (a list or set) limits the autoload to the given column names.
  409. """
  410. dialect.logger.debug("\n *** IBM_DBDialect::reflecttable( "+str(table)+', '+str(include_columns)+' )')
  411. ibm_dbi_conn = connection.connection.connection
  412. schema_name = self.get_default_schema_name(connection)
  413. if table.schema is not None:
  414. ibm_dbi_conn.set_current_schema(table.schema)
  415. schema_name = table.schema
  416. # Append columns to table
  417. columns = ibm_dbi_conn.columns( schema_name, table.name, include_columns)
  418. if not columns:
  419. raise exceptions.NoSuchTableError(table.name)
  420. for col in columns:
  421. (tab_name, col_name, col_id, col_type, def_value, is_nullable, col_size, char_len, num_prec, num_scale) = (
  422. col['TABLE_NAME'].lower(),
  423. col['COLUMN_NAME'].lower(),
  424. col['ORDINAL_POSITION'],
  425. col['TYPE_NAME'],
  426. col['COLUMN_DEF'],
  427. col['IS_NULLABLE'] == 'YES',
  428. col['COLUMN_SIZE'],
  429. col['CHAR_OCTET_LENGTH'],
  430. col['NUM_PREC_RADIX'],
  431. col['DECIMAL_DIGITS']
  432. )
  433. col_args= []
  434. if def_value is not None:
  435. col_args.append(schema.PassiveDefault(sql.text(def_value)))
  436. type = ischema_names.get(col_type, None)
  437. column = schema.Column(col_name, type, nullable=is_nullable, *col_args)
  438. dialect.logger.debug("\n *** column: " + repr(column))
  439. table.append_column(column)
  440. # Define table's primary keys
  441. pkeys = ibm_dbi_conn.primary_keys( True, schema_name, table.name)
  442. for pkey in pkeys:
  443. (pk_schema, pk_table, pk_column, pk_name, key_seq) = (
  444. pkey['TABLE_SCHEM'].lower(),
  445. pkey['TABLE_NAME'].lower(),
  446. pkey['COLUMN_NAME'].lower(),
  447. pkey['PK_NAME'].lower(),
  448. pkey['KEY_SEQ']
  449. )
  450. table.primary_key.add(table.c[pk_column])
  451. # Define table's other indexes
  452. indexes = ibm_dbi_conn.indexes( True, schema_name, table.name)
  453. for idx in indexes:
  454. (idx_schema, idx_table, idx_col, idx_name, idx_id, idx_type, is_unique, ascendent) = (
  455. idx['TABLE_SCHEM'].lower(),
  456. idx['TABLE_NAME'].lower(),
  457. idx['COLUMN_NAME'].lower(),
  458. idx['INDEX_NAME'].lower(),
  459. idx['ORDINAL_POSITION'],
  460. idx['TYPE'],
  461. idx['NON_UNIQUE'] == 0,
  462. idx['ASC_OR_DESC'] == 'A'
  463. )
  464. dialect.logger.debug("\n *** IBM_DBDialect::reflecttable: indexes: " + str(idx))
  465. # Define table's foreign keys
  466. fkeys = ibm_dbi_conn.foreign_keys( True, schema_name, table.name)
  467. for fkey in fkeys:
  468. ( pk_schema, pk_table, pk_column, pk_name, key_seq,
  469. fk_schema, fk_table, fk_column, fk_name) = (
  470. fkey['PKTABLE_SCHEM'].lower(),
  471. fkey['PKTABLE_NAME'].lower(),
  472. fkey['PKCOLUMN_NAME'].lower(),
  473. fkey['PK_NAME'].lower(),
  474. fkey['KEY_SEQ'],
  475. fkey['FKTABLE_SCHEM'].lower(),
  476. fkey['FKTABLE_NAME'].lower(),
  477. fkey['FKCOLUMN_NAME'].lower(),
  478. fkey['FK_NAME'].lower()
  479. )
  480. table.append_constraint(schema.ForeignKeyConstraint( ['%s'%(fk_column)],
  481. ['%s.%s'%(pk_table,pk_column)]))
  482. dialect.logger.debug("\n *** IBM_DBDialect::reflecttable: table: " + repr(table))
  483. # Checks if the DB_API driver error indicates an invalid connection
  484. def is_disconnect(self, ex):
  485. """ Inputs: DB_API driver exception to be checked for invalid connection
  486. Returns: True, if the exception indicates invalid connection, False otherwise.
  487. """
  488. if isinstance(ex, (self.dbapi.ProgrammingError,
  489. self.dbapi.OperationalError)):
  490. is_closed = 'Connection is not active' in str(ex) or \
  491. 'connection is no longer active' in str(ex) or \
  492. 'Connection Resource cannot be found' in str(ex)
  493. return is_closed
  494. else:
  495. return False
  496. # Returns the OID or ROWID column name, if the data server supports it.
  497. def oid_column_name(self, column):
  498. """ Inputs:
  499. Returns: String representing oid, or None if the data server
  500. does not support ROWID.
  501. """
  502. return None
  503. # Returns the converted SA adapter type for a given generic vendor type provided
  504. def type_descriptor(self, typeobj):
  505. """ Inputs: generic type to be converted
  506. Returns: converted adapter type
  507. """
  508. return sa_types.adapt_type(typeobj, colspecs)
  509. # A messenger object for a Dialect that corresponds to a single execution.
  510. # ExecutionContext should have these datamembers:
  511. # connection
  512. # Connection object which can be freely used by default value generators to execute SQL.
  513. # Should reference the same underlying connection/transactional resources of root_connection.
  514. # root_connection
  515. # Connection object which is the source of this ExecutionContext.
  516. # May have close_with_result=True set, in which case it can only be used once.
  517. class IBM_DBExecutionContext(default.DefaultExecutionContext):
  518. """self.statement
  519. String version of the statement to be executed. Is either passed to the constructor,
  520. or must be created from the sql.Compiled object by the time pre_exec() has completed.
  521. """
  522. """self.parameters
  523. bind_processor parameters passed to the execute() method. For compiled statements,
  524. this is a dictionary or list of dictionaries. For textual statements, it should be
  525. in a format suitable for the dialect paramstyle (i.e. dictionary or list of
  526. dictionaries for non positional, list or list of lists/tuples for positional).
  527. """
  528. # Called after the execution of a statement. This method performs any post-processing required.
  529. # If the DBSIExecutionContext contains a compiled statement, the last_insert_ids,
  530. # last_inserted_params, etc. instance variables should be available after this method completes.
  531. def post_exec(self):
  532. """ Inputs: If the ExecutionContext contains a compiled statement,
  533. the last_insert_ids, last_inserted_params, etc. instance variables
  534. should be available after this method completes.
  535. Returns: None
  536. """
  537. if self.compiled.isinsert and \
  538. not self.executemany:
  539. dialect.logger.debug(" > IBM_DBExecutionContext::post_exec: _last_inserted_ids:" + repr(self._last_inserted_ids))
  540. if not len(self._last_inserted_ids) or \
  541. self._last_inserted_ids[0] is None:
  542. self._last_inserted_ids = [self.cursor.last_identity_val]
  543. # Compiles ClauseElement objects into SQL strings.
  544. # DefaultCompiler = Default implementation of Compiled.
  545. # Compiled = a compiled SQL expression.
  546. # The __str__ method of the Compiled object should produce the actual text of the statement.
  547. # Compiled objects are specific to their underlying database dialect, and also may or may not
  548. # be specific to the columns referenced within a particular set of bind parameters.
  549. # In no case should the Compiled object be dependent on the actual values of those
  550. # bind parameters, even though it may reference those values as defaults.
  551. # Compiles ClauseElements into SQL strings.
  552. class IBM_DBCompiler(compiler.DefaultCompiler):
  553. # Generates the limit/offset clause specific/expected by the database vendor
  554. def limit_clause(self, select):
  555. dialect.logger.debug('\n *** IBM_DBCompiler::limit_clause( '+repr(select)+' )')
  556. return ''
  557. # Implicit clause to be inserted when no FROM clause is provided
  558. def default_from(self):
  559. return " FROM SYSIBM.SYSDUMMY1" # DB2 uses SYSIBM.SYSDUMMY1 table for row count
  560. # Generates the SQL string for the SQL function construct.
  561. def visit_function( self , func ):
  562. dialect.logger.debug('\n *** IBM_DBCompiler::visit_function( '+repr(func.name)+' )')
  563. if func.name.upper() == "LENGTH":
  564. return "LENGTH('%s')" % func.compile().params[func.name + '_1']
  565. else:
  566. return compiler.DefaultCompiler.visit_function( self, func )
  567. # Visitor meant to generate/gather DDL for creating DB objects
  568. class IBM_DBSchemaGenerator(compiler.SchemaGenerator):
  569. # Overrides default retrieval in the compiler to handle DB2 booleans
  570. def get_column_default_string(self, column):
  571. default = compiler.SchemaGenerator.get_column_default_string(self, column)
  572. if default == "'False'":
  573. default = '0'
  574. elif default == "'True'":
  575. default = '1'
  576. return default
  577. # Retrieves the column spec (type, attributes: PK, DEFAULT, NULLABLE)
  578. def get_column_specification(self, column, first_pk = False):
  579. """Inputs: Column object to be specified as a string
  580. Boolean indicating whether this is the first column of the primary key
  581. Returns: String, representing the column type and attributes,
  582. including primary key, default values, and whether or not it is nullable.
  583. """
  584. dialect.logger.debug('\n *** IBM_DBSchemaGenerator::get_column_specification( '+repr(column)+' )')
  585. # column-definition: column-name:
  586. col_spec = [self.preparer.format_column(column)]
  587. # data-type:
  588. col_spec.append(column.type.dialect_impl(self.dialect).get_col_spec())
  589. # column-options: "NOT NULL"
  590. if not column.nullable or column.primary_key:
  591. col_spec.append('NOT NULL')
  592. # default-clause:
  593. default = self.get_column_default_string(column)
  594. if default is not None:
  595. col_spec.append('WITH DEFAULT')
  596. default = default.lstrip("'").rstrip("'")
  597. col_spec.append(default)
  598. # generated-column-spec:
  599. # identity-options:
  600. # example: id INT GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
  601. if column.primary_key and \
  602. column.autoincrement and \
  603. isinstance(column.type, sa_types.Integer) and \
  604. not getattr(self, 'has_IDENTITY', False): # allowed only for a single PK
  605. col_spec.append('GENERATED BY DEFAULT')
  606. col_spec.append('AS IDENTITY')
  607. col_spec.append('(START WITH 1)')
  608. self.has_IDENTITY = True # flag the existence of identity PK
  609. column_spec = ' '.join(col_spec)
  610. dialect.logger.debug("\n *** IBM_DBSchemaGenerator::get_column_specification: " + str(column_spec))
  611. return column_spec
  612. # Defines SQL statement to be executed after table creation
  613. def post_create_table(self, table):
  614. if hasattr( self , 'has_IDENTITY' ): # remove identity PK flag once table is created
  615. del self.has_IDENTITY
  616. return ''
  617. # A visitor which generates the DDL used to drop database objects
  618. class IBM_DBSchemaDropper(compiler.SchemaDropper):
  619. """Generates the string of a sequence to be uses in a DDL DROP statement
  620. def visit_sequence(self, sequence):
  621. Inputs: sequence object representing a SQL Named Sequence
  622. Returns: String to be used as a SQL DROP statement for a Named Sequence
  623. """
  624. # A visitor which accepts ColumnDefault objects, produces the dialect-specific
  625. # SQL corresponding to their execution, and executes the SQL, returning the result value.
  626. # DefaultRunners are used internally by Engines and Dialects. Specific database modules
  627. # should provide their own subclasses of DefaultRunner to allow database-specific behavior.
  628. class IBM_DBDefaultRunner(base.DefaultRunner):
  629. """Retrieves the column default value, if it exists.
  630. def get_column_default(self, column): pass
  631. Inputs: Column object to be specified as a string
  632. Returns: column default values as a string, if exists,
  633. None otherwise
  634. """
  635. """Obtains a sequences next value, if it exists, by issuing the appropriate DBSI SQL
  636. def visit_sequence(self, sequence): pass
  637. Inputs: Sequence object representing a SQL Named Sequence
  638. Returns: next value of Sequence database object, if exists,
  639. None otherwise.
  640. """
  641. # Handle quoting and case-folding of identifiers based on options.
  642. class IBM_DBIdentifierPreparer(compiler.IdentifierPreparer):
  643. reserved_words = RESERVED_WORDS
  644. def __init__(self, dialect):
  645. super(IBM_DBIdentifierPreparer, self).__init__(dialect, initial_quote="'", final_quote="'")
  646. # Override the identifier quoting default implementation.
  647. def _requires_quotes(self, value):
  648. return False
  649. # Retrieve data server metadata and connection info.
  650. # It is designed to be used in automated configuration tools that expecte
  651. # to query the user for database and connection information
  652. def descriptor():
  653. """A module-level function that is used by the engine to obtain information
  654. about adapter. It is designed to be used in automated configuration
  655. tools that wish to query the user for database and connection information.
  656. Returns: dictionary with the following key/value pairs:
  657. name: name of the engine, suitable for use in the create_engine function
  658. description: plain description of the engine.
  659. arguments: dictionary describing the name and description
  660. of each parameter used to connect to the underlying DB-API.
  661. """
  662. return { 'name': 'ibm_db_sa',
  663. 'description': 'SQLAlchemy support for IBM Data Servers',
  664. 'arguments': [ ('database', 'Database Name', None),
  665. ('schema', 'Schema name', None),
  666. ('host', 'Host name', None),
  667. ('port', 'Port number', None),
  668. ('user', 'Username', None),
  669. ('password', 'Password', None)
  670. ]
  671. }
  672. # Set IBM_DBDialect instance attributes: SchemaGenerator, SchemaDropper,
  673. # DefaultRunner, Compiler, IdentifierPreparer, logger
  674. dialect = IBM_DBDialect
  675. dialect.schemagenerator = IBM_DBSchemaGenerator
  676. dialect.schemadropper = IBM_DBSchemaDropper
  677. dialect.defaultrunner = IBM_DBDefaultRunner
  678. dialect.statement_compiler = IBM_DBCompiler
  679. dialect.preparer = IBM_DBIdentifierPreparer
  680. dialect.logger = logging.instance_logger(dialect, echoflag=False)