mssql.inc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <?php
  2. /**
  3. * @file
  4. * Define a MigrateSource for importing from Microsoft SQL Server databases.
  5. */
  6. /**
  7. * Implementation of MigrateSource, to handle imports from remote MS SQL Server db servers.
  8. */
  9. class MigrateSourceMSSQL extends MigrateSource {
  10. /**
  11. * Array containing information for connecting to SQL Server:
  12. * servername - Hostname of the SQL Server
  13. * username - Username to connect as
  14. * password - Password for logging in
  15. * database (optional) - Database to select after connecting
  16. *
  17. * @var array
  18. */
  19. protected $configuration;
  20. /**
  21. * The active MS SQL Server connection for this source.
  22. *
  23. * @var resource
  24. */
  25. protected $connection;
  26. /**
  27. * The SQL query from which to obtain data. Is a string.
  28. */
  29. protected $query;
  30. /**
  31. * The result object from executing the query - traversed to process the
  32. * incoming data.
  33. */
  34. protected $result;
  35. /**
  36. * By default, mssql_query fetches all results - severe memory problems with
  37. * big tables. So, we will fetch a batch at a time.
  38. *
  39. * @var int
  40. */
  41. protected $batchSize;
  42. /**
  43. * Return an options array for MS SQL sources.
  44. *
  45. * @param int $batch_size
  46. * Number of rows to pull at once (defaults to 500).
  47. * @param boolean $cache_counts
  48. * Indicates whether to cache counts of source records.
  49. */
  50. static public function options($batch_size, $cache_counts) {
  51. return compact('batch_size', 'cache_counts');
  52. }
  53. /**
  54. * Simple initialization.
  55. */
  56. public function __construct(array $configuration, $query, $count_query,
  57. array $fields, array $options = array()) {
  58. parent::__construct($options);
  59. $this->query = $query;
  60. $this->countQuery = $count_query;
  61. $this->configuration = $configuration;
  62. $this->fields = $fields;
  63. $this->batchSize = isset($options['batch_size']) ? $options['batch_size'] : 500;
  64. }
  65. /**
  66. * Return a string representing the source query.
  67. *
  68. * @return string
  69. */
  70. public function __toString() {
  71. return $this->query;
  72. }
  73. /**
  74. * Connect lazily to the DB server.
  75. */
  76. protected function connect() {
  77. if (!isset($this->connection)) {
  78. if (!extension_loaded('mssql')) {
  79. throw new Exception(t('You must configure the mssql extension in PHP.'));
  80. }
  81. if (isset($this->configuration['port'])) {
  82. $host = $this->configuration['servername'] . ':' . $this->configuration['port'];
  83. }
  84. else {
  85. $host = $this->configuration['servername'];
  86. }
  87. $this->connection = mssql_connect(
  88. $host,
  89. $this->configuration['username'],
  90. $this->configuration['password'],
  91. TRUE);
  92. if (isset($this->configuration['database'])) {
  93. return mssql_select_db($this->configuration['database'], $this->connection);
  94. }
  95. }
  96. }
  97. /**
  98. * Returns a list of fields available to be mapped from the source query.
  99. *
  100. * @return array
  101. * Keys: machine names of the fields (to be passed to addFieldMapping)
  102. * Values: Human-friendly descriptions of the fields.
  103. */
  104. public function fields() {
  105. // The fields are passed to the constructor for this plugin.
  106. return $this->fields;
  107. }
  108. /**
  109. * Return a count of all available source records.
  110. */
  111. public function computeCount() {
  112. migrate_instrument_start('MigrateSourceMSSQL count');
  113. if ($this->connect()) {
  114. $result = mssql_query($this->countQuery);
  115. $count = reset(mssql_fetch_object($result));
  116. }
  117. else {
  118. // Do something else?
  119. $count = FALSE;
  120. }
  121. migrate_instrument_stop('MigrateSourceMSSQL count');
  122. return $count;
  123. }
  124. /**
  125. * Implementation of MigrateSource::performRewind().
  126. */
  127. public function performRewind() {
  128. /*
  129. * Replace :criteria placeholder with idlist or highwater clauses. We
  130. * considered supporting both but it is not worth the complexity. Run twice
  131. * instead.
  132. */
  133. if (!empty($this->idList)) {
  134. $keys = array();
  135. foreach ($this->activeMap->getSourceKey() as $field_name => $field_schema) {
  136. // Allow caller to provide an alias to table containing the primary key.
  137. if (!empty($field_schema['alias'])) {
  138. $field_name = $field_schema['alias'] . '.' . $field_name;
  139. }
  140. $keys[] = $field_name;
  141. }
  142. // TODO: Sanitize. not critical as this is admin supplied data in drush.
  143. $this->query = str_replace(':criteria',
  144. $keys[0] . ' IN (' . implode(',', $this->idList) . ')', $this->query);
  145. }
  146. else {
  147. if (isset($this->highwaterField['name']) && $highwater = $this->activeMigration->getHighwater()) {
  148. if (empty($this->highwaterField['alias'])) {
  149. $highwater_name = $this->highwaterField['name'];
  150. }
  151. else {
  152. $highwater_name = $this->highwaterField['alias'] . '.' . $this->highwaterField['name'];
  153. }
  154. $this->query = str_replace(':criteria', "$highwater_name > '$highwater'", $this->query);
  155. }
  156. else {
  157. // No idlist or highwater. Replace :criteria placeholder with harmless WHERE
  158. // clause instead of empty since we don't know if an AND follows.
  159. $this->query = str_replace(':criteria', '1=1', $this->query);
  160. }
  161. }
  162. migrate_instrument_start('mssql_query');
  163. $this->connect();
  164. $this->result = mssql_query($this->query, $this->connection, $this->batchSize);
  165. migrate_instrument_stop('mssql_query');
  166. }
  167. /**
  168. * Implementation of MigrateSource::getNextRow().
  169. *
  170. * Returns the next row of the result set as an object, dealing with the
  171. * difference between the end of the batch and the end of all data.
  172. */
  173. public function getNextRow() {
  174. $row = mssql_fetch_object($this->result);
  175. // Might be totally out of data, or just out of this batch - request another
  176. // batch and see
  177. if (!is_object($row)) {
  178. mssql_fetch_batch($this->result);
  179. $row = mssql_fetch_object($this->result);
  180. }
  181. if (is_object($row)) {
  182. return $row;
  183. }
  184. else {
  185. return NULL;
  186. }
  187. }
  188. }