oracle.inc 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <?php
  2. /**
  3. * @file
  4. * Define a MigrateSource class for importing from Oracle databases.
  5. */
  6. /**
  7. * Implementation of MigrateSource, to handle imports from remote Oracle servers.
  8. */
  9. class MigrateSourceOracle extends MigrateSource {
  10. /**
  11. * Array containing information for connecting to Oracle:
  12. * username - Username to connect as
  13. * password - Password for logging in
  14. * connection_string - See http://us.php.net/manual/en/function.oci-connect.php.
  15. *
  16. * @var array
  17. */
  18. protected $configuration;
  19. /**
  20. * The active Oracle connection for this source.
  21. *
  22. * @var resource
  23. */
  24. protected $connection;
  25. public function getConnection() {
  26. return $this->connection;
  27. }
  28. /**
  29. * The SQL query from which to obtain data. Is a string.
  30. */
  31. protected $query;
  32. /**
  33. * The result object from executing the query - traversed to process the
  34. * incoming data.
  35. */
  36. protected $result;
  37. /**
  38. * Character set to use in retrieving data.
  39. *
  40. * @var string
  41. */
  42. protected $characterSet;
  43. /**
  44. * Return an options array for Oracle sources.
  45. *
  46. * @param string $character_set
  47. * Character set to use in retrieving data. Defaults to 'UTF8'.
  48. * @param boolean $cache_counts
  49. * Indicates whether to cache counts of source records.
  50. */
  51. static public function options($character_set = 'UTF8', $cache_counts = FALSE) {
  52. return compact('character_set', 'cache_counts');
  53. }
  54. /**
  55. * Simple initialization.
  56. */
  57. public function __construct(array $configuration, $query, $count_query,
  58. array $fields, array $options = array()) {
  59. parent::__construct($options);
  60. $this->query = $query;
  61. $this->countQuery = $count_query;
  62. $this->configuration = $configuration;
  63. $this->fields = $fields;
  64. if (empty($options['character_set'])) {
  65. $this->characterSet = 'UTF8';
  66. }
  67. else {
  68. $this->characterSet = $options['character_set'];
  69. }
  70. }
  71. /**
  72. * Return a string representing the source query.
  73. *
  74. * @return string
  75. */
  76. public function __toString() {
  77. return $this->query;
  78. }
  79. /**
  80. * Connect lazily to the DB server.
  81. */
  82. protected function connect() {
  83. if (!isset($this->connection)) {
  84. if (!extension_loaded('oci8')) {
  85. throw new Exception(t('You must configure the oci8 extension in PHP.'));
  86. }
  87. $this->connection = oci_connect($this->configuration['username'],
  88. $this->configuration['password'], $this->configuration['connection_string'],
  89. $this->characterSet);
  90. }
  91. if ($this->connection) {
  92. return TRUE;
  93. }
  94. else {
  95. $e = oci_error();
  96. throw new Exception($e['message']);
  97. return FALSE;
  98. }
  99. }
  100. /**
  101. * Returns a list of fields available to be mapped from the source query.
  102. *
  103. * @return array
  104. * Keys: machine names of the fields (to be passed to addFieldMapping)
  105. * Values: Human-friendly descriptions of the fields.
  106. */
  107. public function fields() {
  108. // The fields are passed to the constructor for this plugin.
  109. return $this->fields;
  110. }
  111. /**
  112. * Return a count of all available source records.
  113. */
  114. public function computeCount() {
  115. migrate_instrument_start('MigrateSourceOracle count');
  116. if ($this->connect()) {
  117. $statement = oci_parse($this->connection, $this->countQuery);
  118. if (!$statement) {
  119. $e = oci_error($this->connection);
  120. throw new Exception($e['message'] . "\n" . $e['sqltext']);
  121. }
  122. $result = oci_execute($statement);
  123. if (!$result) {
  124. $e = oci_error($statement);
  125. throw new Exception($e['message'] . "\n" . $e['sqltext']);
  126. }
  127. $count_array = oci_fetch_array($statement);
  128. $count = reset($count_array);
  129. }
  130. else {
  131. // Do something else?
  132. $count = FALSE;
  133. }
  134. migrate_instrument_stop('MigrateSourceOracle count');
  135. return $count;
  136. }
  137. /**
  138. * Implementation of MigrateSource::performRewind().
  139. */
  140. public function performRewind() {
  141. $keys = array();
  142. foreach ($this->activeMap->getSourceKey() as $field_name => $field_schema) {
  143. // Allow caller to provide an alias to table containing the primary key.
  144. if (!empty($field_schema['alias'])) {
  145. $field_name = $field_schema['alias'] . '.' . $field_name;
  146. }
  147. $keys[] = $field_name;
  148. }
  149. /*
  150. * Replace :criteria placeholder with idlist or highwater clauses. We
  151. * considered supporting both but it is not worth the complexity. Run twice
  152. * instead.
  153. */
  154. if (!empty($this->idList)) {
  155. // TODO: Sanitize. not critical as this is admin supplied data in drush.
  156. $this->query = str_replace(':criteria',
  157. $keys[0] . ' IN (' . implode(',', $this->idList) . ')', $this->query);
  158. }
  159. else {
  160. if (isset($this->highwaterField['name']) && $highwater = $this->activeMigration->getHighwater()) {
  161. if (empty($this->highwaterField['alias'])) {
  162. $highwater_name = $this->highwaterField['name'];
  163. }
  164. else {
  165. $highwater_name = $this->highwaterField['alias'] . '.' . $this->highwaterField['name'];
  166. }
  167. $this->query = str_replace(':criteria', "$highwater_name > '$highwater'", $this->query);
  168. }
  169. else {
  170. // No idlist or highwater. Replace :criteria placeholder with harmless WHERE
  171. // clause instead of empty since we don't know if an AND follows.
  172. $this->query = str_replace(':criteria', '1=1', $this->query);
  173. }
  174. }
  175. migrate_instrument_start('oracle_query');
  176. $this->connect();
  177. $this->result = oci_parse($this->connection, $this->query);
  178. if (!$this->result) {
  179. $e = oci_error($this->connection);
  180. throw new Exception($e['message'] . "\n" . $e['sqltext']);
  181. }
  182. $status = oci_execute($this->result);
  183. if (!$status) {
  184. $e = oci_error($this->result);
  185. throw new Exception($e['message'] . "\n" . $e['sqltext']);
  186. }
  187. migrate_instrument_stop('oracle_query');
  188. }
  189. /**
  190. * Implementation of MigrateSource::getNextRow().
  191. *
  192. * Returns the next row of the result set as an object, making sure NULLs are
  193. * represented as PHP NULLs and that LOBs are returned directly without special
  194. * handling.
  195. */
  196. public function getNextRow() {
  197. $row = oci_fetch_array($this->result, OCI_ASSOC | OCI_RETURN_NULLS | OCI_RETURN_LOBS);
  198. if (!empty($row)) {
  199. return (object)$row;
  200. }
  201. else {
  202. return FALSE;
  203. }
  204. }
  205. }