PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/package/wp/2.9.2/wordpress/wp-includes/wp-db/sqlsrv/translations/translations.php

https://github.com/wpms/wp-sqlsrv
PHP | 1537 lines | 1037 code | 86 blank | 414 comment | 180 complexity | 6d6280ff07bf980cc35704e2591556dc MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. require_once(dirname(__FILE__) . '/fields_map.php');
  3. /**
  4. * SQL Dialect Translations
  5. *
  6. * @category MSSQL
  7. * @package MySQL_Translations
  8. * @author A.Garcia & A.Gentile
  9. * */
  10. class SQL_Translations
  11. {
  12. /**
  13. * Field Mapping
  14. *
  15. * @since 2.7.1
  16. * @access private
  17. * @var array
  18. */
  19. var $fields_map = null;
  20. /**
  21. * Was this query prepared?
  22. *
  23. * @since 2.7.1
  24. * @access private
  25. * @var bool
  26. */
  27. var $prepared = false;
  28. /**
  29. * Update query?
  30. *
  31. * @since 2.7.1
  32. * @access private
  33. * @var bool
  34. */
  35. var $update_query = false;
  36. /**
  37. * Insert query?
  38. *
  39. * @since 2.7.1
  40. * @access private
  41. * @var bool
  42. */
  43. var $insert_query = false;
  44. /**
  45. * Delete query?
  46. *
  47. * @since 2.7.1
  48. * @access private
  49. * @var bool
  50. */
  51. var $delete_query = false;
  52. /**
  53. * Prepare arguments
  54. *
  55. * @since 2.7.1
  56. * @access private
  57. * @var array
  58. */
  59. var $prepare_args = array();
  60. /**
  61. * Update Data
  62. *
  63. * @since 2.7.1
  64. * @access private
  65. * @var array
  66. */
  67. var $update_data = array();
  68. /**
  69. * Limit Info
  70. *
  71. * @since 2.7.1
  72. * @access private
  73. * @var array
  74. */
  75. var $limit = array();
  76. /**
  77. * Update Data
  78. *
  79. * @since 2.7.1
  80. * @access private
  81. * @var array
  82. */
  83. var $translation_changes = array();
  84. /**
  85. * Azure
  86. * Are we dealing with a SQL Azure DB?
  87. *
  88. * @since 2.7.1
  89. * @access public
  90. * @var bool
  91. */
  92. var $azure = false;
  93. /**
  94. * Preceeding query
  95. * Sometimes we need to issue a query
  96. * before the original query
  97. *
  98. * @since 2.8.5
  99. * @access public
  100. * @var mixed
  101. */
  102. var $preceeding_query = false;
  103. /**
  104. * Following query
  105. * Sometimes we need to issue a query
  106. * right after the original query
  107. *
  108. * @since 2.8.5
  109. * @access public
  110. * @var mixed
  111. */
  112. var $following_query = false;
  113. /**
  114. * Should we verify update/insert queries?
  115. *
  116. * @since 2.8.5
  117. * @access public
  118. * @var mixed
  119. */
  120. var $verify = true;
  121. /**
  122. * php4 style call to constructor.
  123. *
  124. * @since 2.7.1
  125. *
  126. */
  127. function SQL_Translations()
  128. {
  129. return $this->__construct();
  130. }
  131. /**
  132. * Assign fields_map as a new Fields_map object
  133. *
  134. * PHP5 style constructor for compatibility with PHP5.
  135. *
  136. * @since 2.7.1
  137. */
  138. function __construct()
  139. {
  140. $this->fields_map = new Fields_map();
  141. }
  142. /**
  143. * MySQL > MSSQL Query Translation
  144. * Processes smaller translation sub-functions
  145. *
  146. * @since 2.7.1
  147. *
  148. * @param string $query Query coming in
  149. *
  150. * @return string Translated Query
  151. */
  152. function translate($query)
  153. {
  154. $this->limit = array();
  155. $this->verify = true;
  156. $this->insert_query = false;
  157. $this->delete_query = false;
  158. $this->update_query = false;
  159. $this->preceeding_query = false;
  160. $this->following_query = false;
  161. if ( stripos($query, 'INSERT') === 0 ) {
  162. $this->insert_query = true;
  163. }
  164. if ( stripos($query, 'DELETE') === 0 ) {
  165. $this->delete_query = true;
  166. }
  167. // Was this query prepared?
  168. if ( strripos($query, '--PREPARE') !== FALSE ) {
  169. $query = str_replace('--PREPARE', '', $query);
  170. $this->prepared = TRUE;
  171. } else {
  172. $this->prepared = FALSE;
  173. }
  174. // Update Query?
  175. if ( stripos($query, 'UPDATE') === 0 ) {
  176. $this->update_query = true;
  177. }
  178. // Do we have serialized arguments?
  179. if ( strripos($query, '--SERIALIZED') !== FALSE ) {
  180. $query = str_replace('--SERIALIZED', '', $query);
  181. if ($this->insert_query) {
  182. $query = $this->on_duplicate_key($query);
  183. }
  184. $query = $this->translate_general($query);
  185. return $query;
  186. }
  187. $query = trim($query);
  188. $sub_funcs = array(
  189. 'translate_general',
  190. 'translate_date_add',
  191. 'translate_if_stmt',
  192. 'translate_sqlcalcrows',
  193. 'translate_limit',
  194. 'translate_now_datetime',
  195. 'translate_distinct_orderby',
  196. 'translate_sort_casting',
  197. 'translate_column_type',
  198. 'translate_remove_groupby',
  199. 'translate_insert_nulltime',
  200. 'translate_incompat_data_type',
  201. 'translate_create_queries',
  202. );
  203. // Perform translations and record query changes.
  204. $this->translation_changes = array();
  205. foreach ( $sub_funcs as $sub_func ) {
  206. $old_query = $query;
  207. $query = $this->$sub_func($query);
  208. if ( $old_query !== $query ) {
  209. $this->translation_changes[] = $sub_func;
  210. $this->translation_changes[] = $query;
  211. $this->translation_changes[] = $old_query;
  212. }
  213. }
  214. if ( $this->insert_query ) {
  215. $query = $this->on_duplicate_key($query);
  216. $query = $this->split_insert_values($query);
  217. }
  218. if ( $this->prepared && $this->insert_query && $this->verify ) {
  219. if ( is_array($query) ) {
  220. foreach ($query as $k => $v) {
  221. $query[$k] = $this->verify_insert($v);
  222. }
  223. } else {
  224. $query = $this->verify_insert($query);
  225. }
  226. }
  227. if ( $this->update_query && $this->verify ) {
  228. $query = $this->verify_update($query);
  229. }
  230. return $query;
  231. }
  232. /**
  233. * More generalized information gathering queries
  234. *
  235. * @since 2.7.1
  236. *
  237. * @param string $query Query coming in
  238. *
  239. * @return string Translated Query
  240. */
  241. function translate_general($query)
  242. {
  243. // SERVER VERSION
  244. if ( stripos($query, 'SELECT VERSION()' ) === 0) {
  245. $query = substr_replace($query, 'SELECT @@VERSION', 0, 16);
  246. }
  247. // SQL_MODE NO EQUIV
  248. if ( stripos($query, "SHOW VARIABLES LIKE 'sql_mode'" ) === 0) {
  249. $query = '';
  250. }
  251. // LAST INSERT ID
  252. if ( stripos($query, 'LAST_INSERT_ID()') > 0 ) {
  253. $start_pos = stripos($query, 'LAST_INSERT_ID()');
  254. $query = substr_replace($query, '@@IDENTITY', $start_pos, 16);
  255. }
  256. // SHOW TABLES
  257. if ( strtolower($query) === 'show tables;' ) {
  258. $query = str_ireplace('show tables',"select name from SYSOBJECTS where TYPE = 'U' order by NAME",$query);
  259. }
  260. if ( stripos($query, 'show tables like ') === 0 ) {
  261. $end_pos = strlen($query);
  262. $param = substr($query, 17, $end_pos - 17);
  263. $query = 'SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE ' . $param;
  264. }
  265. // SET NAMES doesn't exist in T-SQL
  266. if ( strtolower($query) == "set names 'utf8'" ) {
  267. $query = "";
  268. }
  269. // SHOW COLUMNS
  270. if ( stripos($query, 'SHOW COLUMNS FROM ') === 0 ) {
  271. $end_pos = strlen($query);
  272. $param = substr($query, 18, $end_pos - 18);
  273. $param = "'". trim($param, "'") . "'";
  274. $query = 'SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ' . $param;
  275. }
  276. // SHOW INDEXES - issue with sql azure trying to fix....sys.sysindexes is coming back as invalid onject name
  277. if ( stripos($query, 'SHOW INDEXES FROM ') === 0 ) {
  278. return $query;
  279. $table = substr($query, 18);
  280. $query = "SELECT sys.sysindexes.name AS IndexName
  281. FROM sysobjects
  282. JOIN sys.key_constraints ON parent_object_id = sys.sysobjects.id
  283. JOIN sys.sysindexes ON sys.sysindexes.id = sys.sysobjects.id and sys.key_constraints.unique_index_id = sys.sysindexes.indid
  284. JOIN sys.index_columns ON sys.index_columns.object_id = sys.sysindexes.id and sys.index_columns.index_id = sys.sysindexes.indid
  285. JOIN sys.syscolumns ON sys.syscolumns.id = sys.sysindexes.id AND sys.index_columns.column_id = sys.syscolumns.colid
  286. WHERE sys.sysobjects.type = 'u'
  287. AND sys.sysobjects.name = '{$table}'";
  288. }
  289. // USE INDEX
  290. if ( stripos($query, 'USE INDEX (') !== FALSE) {
  291. $start_pos = stripos($query, 'USE INDEX (');
  292. $end_pos = $this->get_matching_paren($query, $start_pos + 11);
  293. $params = substr($query, $start_pos + 11, $end_pos - ($start_pos + 11));
  294. $params = explode(',', $params);
  295. foreach ($params as $k => $v) {
  296. $params[$k] = trim($v);
  297. foreach ($this->fields_map->read() as $table => $fields) {
  298. if ( is_array($fields) ) {
  299. foreach ($fields as $field_name => $field_meta) {
  300. if ( $field_name == $params[$k] ) {
  301. $params[$k] = $table . '_' . $params[$k];
  302. }
  303. }
  304. }
  305. }
  306. }
  307. $params = implode(',', $params);
  308. $query = substr_replace($query, 'WITH (INDEX(' . $params . '))', $start_pos, ($end_pos + 1) - $start_pos);
  309. }
  310. // DESCRIBE - this is pretty darn close to mysql equiv, however it will need to have a flag to modify the result set
  311. // this and SHOW INDEX FROM are used in WP upgrading. The problem is that WP will see the different data types and try
  312. // to alter the table thinking an upgrade is necessary. So the result set from this query needs to be modified using
  313. // the field_mapping to revert column types back to their mysql equiv to fool WP.
  314. if ( stripos($query, 'DESCRIBE ') === 0 ) {
  315. return $query;
  316. $table = substr($query, 9);
  317. $query = $this->describe($table);
  318. }
  319. // DROP TABLES
  320. if ( stripos($query, 'DROP TABLE IF EXISTS ') === 0 ) {
  321. $table = substr($query, 21, strlen($query) - 21);
  322. $query = 'DROP TABLE ' . $table;
  323. } elseif ( stripos($query, 'DROP TABLE ') === 0 ) {
  324. $table = substr($query, 11, strlen($query) - 11);
  325. $query = 'DROP TABLE ' . $table;
  326. }
  327. // REGEXP - not supported in TSQL
  328. if ( stripos($query, 'REGEXP') > 0 ) {
  329. if ( $this->delete_query && stripos($query, '^rss_[0-9a-f]{32}(_ts)?$') > 0 ) {
  330. $start_pos = stripos($query, 'REGEXP');
  331. $query = substr_replace($query, "LIKE 'rss_'", $start_pos);
  332. }
  333. }
  334. // TICKS
  335. $query = str_replace('`', '', $query);
  336. // Computed
  337. // This is done as the SQLSRV driver doesn't seem to set a property value for computed
  338. // selected columns, thus WP doesn't have anything to work with.
  339. $query = str_ireplace('SELECT COUNT(*)', 'SELECT COUNT(*) as Computed', $query);
  340. $query = str_ireplace('SELECT COUNT(1)', 'SELECT COUNT(1) as Computed', $query);
  341. // Turn on IDENTITY_INSERT for Importing inserts or category/tag adds that are
  342. // trying to explicitly set and IDENTITY column (WPMU)
  343. if ($this->insert_query) {
  344. $tables = array(
  345. $this->prefix . 'posts' => 'id',
  346. $this->prefix . 'terms' => 'term_id',
  347. );
  348. foreach ($tables as $table => $pid) {
  349. if (stristr($query, 'INTO ' . $table) !== FALSE) {
  350. $strlen = strlen($table);
  351. $start_pos = stripos($query, $table) + $strlen;
  352. $start_pos = stripos($query, '(', $start_pos);
  353. $end_pos = $this->get_matching_paren($query, $start_pos + 1);
  354. $params = substr($query, $start_pos + 1, $end_pos - ($start_pos + 1));
  355. $params = explode(',', $params);
  356. $found = false;
  357. foreach ($params as $k => $v) {
  358. if (strtolower($v) === $pid) {
  359. $found = true;
  360. }
  361. }
  362. if ($found) {
  363. $this->preceeding_query = "SET IDENTITY_INSERT $table ON";
  364. $this->following_query = "SET IDENTITY_INSERT $table OFF";
  365. }
  366. }
  367. }
  368. }
  369. // UPDATE queries trying to change an IDENTITY column this happens
  370. // for cat/tag adds (WPMU) e.g. UPDATE wp_1_terms SET term_id = 5 WHERE term_id = 3330
  371. if ($this->update_query) {
  372. $tables = array(
  373. $this->prefix . 'terms' => 'term_id',
  374. );
  375. foreach ($tables as $table => $pid) {
  376. if (stristr($query, $table . ' SET ' . $pid) !== FALSE) {
  377. preg_match_all("^=\s\d+^", $query, $matches);
  378. if (!empty($matches) && count($matches[0]) == 2) {
  379. $to = trim($matches[0][0], '= ');
  380. $from = trim($matches[0][1], '= ');
  381. $this->preceeding_query = "SET IDENTITY_INSERT $table ON";
  382. // find a better way to get columns (field mapping doesn't grab all)
  383. $query = "INSERT INTO $table (term_id,name,slug,term_group) SELECT $to,name,slug,term_group FROM $table WHERE $pid = $from";
  384. $this->following_query = array("DELETE $table WHERE $pid = $from","SET IDENTITY_INSERT $table OFF");
  385. $this->verify = false;
  386. }
  387. }
  388. }
  389. }
  390. return $query;
  391. }
  392. /**
  393. * Changes for DATE_ADD and INTERVAL
  394. *
  395. * @since 2.7.1
  396. *
  397. * @param string $query Query coming in
  398. *
  399. * @return string Translated Query
  400. */
  401. function translate_date_add($query)
  402. {
  403. $query = preg_replace('/date_add\((.*?),.*?([0-9]+?) (.*?)\)/i', 'DATEADD(\3,\2,\1)', $query);
  404. $query = preg_replace('/date_sub\((.*?),.*?([0-9]+?) (.*?)\)/i', 'DATEADD(\3,-\2,\1)', $query);
  405. return $query;
  406. }
  407. /**
  408. * Removing Unnecessary IF statement that T-SQL doesn't play nice with
  409. *
  410. * @since 2.7.1
  411. *
  412. * @param string $query Query coming in
  413. *
  414. * @return string Translated Query
  415. */
  416. function translate_if_stmt($query)
  417. {
  418. if ( stripos($query, 'IF (DATEADD(') > 0 ) {
  419. $start_pos = stripos($query, 'DATEADD(');
  420. $end_pos = $this->get_matching_paren($query, $start_pos + 8);
  421. $stmt = substr($query, $start_pos, ($end_pos - $start_pos)) . ') >= getdate() THEN 1 ELSE 0 END)';
  422. $start_pos = stripos($query, 'IF (');
  423. $end_pos = $this->get_matching_paren($query, ($start_pos+6))+1;
  424. $query = substr_replace($query, '(CASE WHEN ' . $stmt, $start_pos, ($end_pos - $start_pos));
  425. }
  426. return $query;
  427. }
  428. /**
  429. * SQL_CALC_FOUND_ROWS does not exist in T-SQL
  430. *
  431. * @since 2.7.1
  432. *
  433. * @param string $query Query coming in
  434. *
  435. * @return string Translated Query
  436. */
  437. function translate_sqlcalcrows($query)
  438. {
  439. if (stripos($query, 'SQL_CALC_FOUND_ROWS') > 0 ) {
  440. $sql_calc_pos = stripos($query, 'SQL_CALC_FOUND_ROWS');
  441. $from_pos = stripos($query, 'FROM');
  442. $query = substr_replace($query,'* ', $sql_calc_pos, ($from_pos - $sql_calc_pos));
  443. }
  444. // catch the next query.
  445. if ( stripos($query, 'FOUND_ROWS()') > 0 ) {
  446. $from_pos = stripos($this->previous_query, 'FROM');
  447. $where_pos = stripos($this->previous_query, 'WHERE');
  448. $from_str = trim(substr($this->previous_query, $from_pos, ($where_pos - $from_pos)));
  449. $order_by_pos = stripos($this->previous_query, 'ORDER BY');
  450. $where_str = trim(substr($this->previous_query, $where_pos, ($order_by_pos - $where_pos)));
  451. $query = str_ireplace('FOUND_ROWS()', 'COUNT(1) as Computed ' . $from_str . ' ' . $where_str, $query);
  452. }
  453. return $query;
  454. }
  455. /**
  456. * Changing LIMIT to TOP...mimicking offset while possible with rownum, it has turned
  457. * out to be very problematic as depending on the original query, the derived table
  458. * will have a lot of problems with columns names, ordering and what not.
  459. *
  460. * @since 2.7.1
  461. *
  462. * @param string $query Query coming in
  463. *
  464. * @return string Translated Query
  465. */
  466. function translate_limit($query)
  467. {
  468. if ( (stripos($query,'SELECT') !== 0 && stripos($query,'SELECT') !== FALSE)
  469. && (stripos($query,'UPDATE') !== 0 && stripos($query,'UPDATE') !== FALSE) ) {
  470. return $query;
  471. }
  472. $pattern = '/LIMIT\s*(\d+)((\s*,?\s*)(\d+)*)$/is';
  473. $matched = preg_match($pattern, $query, $limit_matches);
  474. if ( $matched == 0 ) {
  475. return $query;
  476. }
  477. // Remove the LIMIT statement
  478. $true_offset = false;
  479. $query = preg_replace($pattern, '', $query);
  480. if ( stripos($query,'DELETE') === 0 ) {
  481. return $query;
  482. }
  483. // Check for true offset
  484. if ( count($limit_matches) == 5 && $limit_matches[1] != '0' ) {
  485. $true_offset = true;
  486. } elseif ( count($limit_matches) == 5 && $limit_matches[1] == '0' ) {
  487. $limit_matches[1] = $limit_matches[4];
  488. }
  489. // Rewrite the query.
  490. if ( $true_offset === false ) {
  491. if ( stripos($query, 'DISTINCT') > 0 ) {
  492. $query = str_ireplace('DISTINCT', 'DISTINCT TOP ' . $limit_matches[1] . ' ', $query);
  493. } else {
  494. $query = str_ireplace('DELETE ', 'DELETE TOP ' . $limit_matches[1] . ' ', $query);
  495. $query = str_ireplace('SELECT ', 'SELECT TOP ' . $limit_matches[1] . ' ', $query);
  496. }
  497. } else {
  498. $limit_matches[1] = (int) $limit_matches[1];
  499. $limit_matches[4] = (int) $limit_matches[4];
  500. $this->limit = array(
  501. 'from' => $limit_matches[1],
  502. 'to' => $limit_matches[4]
  503. );
  504. }
  505. return $query;
  506. }
  507. /**
  508. * Replace From UnixTime and now()
  509. *
  510. * @since 2.7.1
  511. *
  512. * @param string $query Query coming in
  513. *
  514. * @return string Translated Query
  515. */
  516. function translate_now_datetime($query)
  517. {
  518. $replacement = 'getdate()';
  519. $query = preg_replace('/(from_unixtime|unix_timestamp)\s*\(([^\)]*)\)/i', $replacement, $query);
  520. $query = str_ireplace('NOW()', $replacement, $query);
  521. // REPLACE dayofmonth which doesn't exist in T-SQL
  522. $check = $query;
  523. $query = preg_replace('/dayofmonth\((.*?)\)/i', 'DATEPART(DD,\1)',$query);
  524. if ($check !== $query) {
  525. $as_array = $this->get_as_fields($query);
  526. if (empty($as_array)) {
  527. $query = str_ireplace('FROM','as dom FROM',$query);
  528. }
  529. }
  530. return $query;
  531. }
  532. /**
  533. * Order By within a Select Distinct needs to have an field for every alias
  534. *
  535. * @since 2.7.1
  536. *
  537. * @param string $query Query coming in
  538. *
  539. * @return string Translated Query
  540. */
  541. function translate_distinct_orderby($query)
  542. {
  543. if ( stripos($query, 'DISTINCT') > 0 ) {
  544. if ( stripos($query, 'ORDER') > 0 ) {
  545. $order_pos = stripos($query, 'ORDER');
  546. if ( stripos($query, 'BY', $order_pos) > $order_pos ) {
  547. $fields = $this->get_as_fields($query);
  548. $ob = stripos($query, 'BY', $order_pos);
  549. if ( stripos($query, ' ASC', $ob) > 0 ) {
  550. $ord = stripos($query, ' ASC', $ob);
  551. }
  552. if ( stripos($query, ' DESC', $ob) > 0 ) {
  553. $ord = stripos($query, ' DESC', $ob);
  554. }
  555. $str = 'BY ';
  556. $str .= implode(', ',$fields);
  557. $query = substr_replace($query, $str, $ob, ($ord-$ob));
  558. }
  559. }
  560. }
  561. return $query;
  562. }
  563. /**
  564. * To sort text fields they need to be first cast as varchar
  565. *
  566. * @since 2.7.1
  567. *
  568. * @param string $query Query coming in
  569. *
  570. * @return string Translated Query
  571. */
  572. function translate_sort_casting($query)
  573. {
  574. if ( in_array('translate_limit', $this->translation_changes)) {
  575. //return $query;
  576. }
  577. if ( stripos($query, 'ORDER') > 0 ) {
  578. $order_pos = stripos($query, 'ORDER');
  579. if ( stripos($query, 'BY', $order_pos) == ($order_pos + 6) && stripos($query, 'OVER(', $order_pos - 5) != ($order_pos - 5)) {
  580. $ob = stripos($query, 'BY', $order_pos);
  581. if ( stripos($query,' ASC', $ob) > 0 ) {
  582. $ord = stripos($query, ' ASC', $ob);
  583. }
  584. if ( stripos($query,' DESC', $ob) > 0 ) {
  585. $ord = stripos($query, ' DESC', $ob);
  586. }
  587. $params = substr($query, ($ob + 3), ($ord - ($ob + 3)));
  588. $params = preg_split('/[\s,]+/', $params);
  589. $p = array();
  590. foreach ( $params as $value ) {
  591. $value = str_replace(',', '', $value);
  592. if ( !empty($value) ) {
  593. $p[] = $value;
  594. }
  595. }
  596. $str = '';
  597. foreach ($p as $v ) {
  598. $match = false;
  599. foreach( $this->fields_map->read() as $table => $table_fields ) {
  600. if ( is_array($table_fields) ) {
  601. foreach ( $table_fields as $field => $field_meta) {
  602. if ($field_meta['type'] == 'text') {
  603. if ( $v == $table . '.' . $field || $v == $field) {
  604. $match = true;
  605. }
  606. }
  607. }
  608. }
  609. }
  610. if ( $match ) {
  611. $str .= 'cast(' . $v . ' as varchar(255)), ';
  612. } else {
  613. $str .= $v . ', ';
  614. }
  615. }
  616. $str = rtrim($str, ', ');
  617. $query = substr_replace($query, $str, ($ob + 3), ($ord - ($ob + 3)));
  618. }
  619. }
  620. return $query;
  621. }
  622. /**
  623. * Meta key fix. \_% to [_]%
  624. *
  625. * @since 2.7.1
  626. *
  627. * @param string $query Query coming in
  628. *
  629. * @return string Translated Query
  630. */
  631. function translate_column_type($query)
  632. {
  633. if ( stripos($query, "LIKE '\_%'") > 0 ) {
  634. $start_pos = stripos($query, "LIKE '\_%'");
  635. $end_pos = $start_pos + 10;
  636. $str = "LIKE '[_]%'";
  637. $query = substr_replace($query, $str, $start_pos, ($end_pos - $start_pos));
  638. }
  639. return $query;
  640. }
  641. /**
  642. * Remove group by stmt in certain queries as T-SQL will
  643. * want all column names to execute query properly
  644. *
  645. * FIXES: Column 'wp_posts.post_author' is invalid in the select list because
  646. * it is not contained in either an aggregate function or the GROUP BY clause.
  647. *
  648. * @since 2.7.1
  649. *
  650. * @param string $query Query coming in
  651. *
  652. * @return string Translated Query
  653. */
  654. function translate_remove_groupby($query)
  655. {
  656. $query = str_ireplace("GROUP BY {$this->prefix}posts.ID ", ' ', $query);
  657. // Fixed query for archives widgets.
  658. $query = str_ireplace(
  659. 'GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC',
  660. 'GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY month DESC, year DESC',
  661. $query
  662. );
  663. return $query;
  664. }
  665. /**
  666. * When INSERTING 0000-00-00 00:00:00 or '' for datetime SQL Server says wtf
  667. * because it's null value begins at 1900-01-01...so lets change this to current time.
  668. *
  669. * @since 2.7.1
  670. *
  671. * @param string $query Query coming in
  672. *
  673. * @return string Translated Query
  674. */
  675. function translate_insert_nulltime($query)
  676. {
  677. if ( stripos($query, 'INSERT') === 0 ) {
  678. // Lets grab the fields to be inserted into and their position
  679. // based on the csv.
  680. $first_paren = stripos($query, '(', 11) + 1;
  681. $last_paren = $this->get_matching_paren($query, $first_paren);
  682. $fields = explode(',',substr($query, $first_paren, ($last_paren - $first_paren)));
  683. $date_fields = array();
  684. $date_fields_map = $this->fields_map->by_type('date');
  685. foreach ($fields as $key => $field ) {
  686. $field = trim($field);
  687. if ( in_array($field, $date_fields_map) ) {
  688. $date_fields[] = array('pos' => $key, 'field' => $field);
  689. }
  690. }
  691. // we have date fields to check
  692. if ( count($date_fields) > 0 ) {
  693. // we need to get the values
  694. $values_pos = stripos($query, 'VALUES');
  695. $first_paren = stripos($query, '(', $values_pos);
  696. $last_paren = $this->get_matching_paren($query, ($first_paren + 1));
  697. $values = explode(',',substr($query, ($first_paren+1), ($last_paren-($first_paren+1))));
  698. foreach ( $date_fields as $df ) {
  699. $v = trim($values[$df['pos']]);
  700. $quote = ( stripos($v, "'0000-00-00 00:00:00'") === 0 || $v === "''" ) ? "'" : '';
  701. if ( stripos($v, '0000-00-00 00:00:00') === 0
  702. || stripos($v, "'0000-00-00 00:00:00'") === 0
  703. || $v === "''" ) {
  704. if ( stripos($df['field'], 'gmt') > 0 ) {
  705. $v = $quote.gmdate('Y-m-d H:i:s').$quote;
  706. } else {
  707. $v = $quote.date('Y-m-d H:i:s').$quote;
  708. }
  709. }
  710. $values[$df['pos']] = $v;
  711. }
  712. $str = implode(',', $values);
  713. $query = substr_replace($query, $str, ($first_paren+1), ($last_paren-($first_paren+1)));
  714. }
  715. }
  716. return $query;
  717. }
  718. /**
  719. * The data types text and varchar are incompatible in the equal to operator.
  720. * TODO: Have a check for the appropriate table of the field to avoid collision
  721. *
  722. * @since 2.7.1
  723. *
  724. * @param string $query Query coming in
  725. *
  726. * @return string Translated Query
  727. */
  728. function translate_incompat_data_type($query)
  729. {
  730. // Lets check to make sure this is a SELECT query.
  731. if ( stripos($query, 'SELECT') === 0 || stripos($query, 'DELETE') === 0 ) {
  732. $operators = array(
  733. '=' => 'LIKE',
  734. '!=' => 'NOT LIKE',
  735. '<>' => 'NOT LIKE'
  736. );
  737. foreach($this->fields_map->read() as $table => $table_fields) {
  738. if (is_array($table_fields)) {
  739. foreach ($table_fields as $field => $field_meta) {
  740. if ( $field_meta['type'] == 'text' ) {
  741. foreach($operators as $oper => $val) {
  742. $query = str_ireplace(
  743. $table . '.' . $field . ' ' . $oper,
  744. $table . '.' . $field . ' ' . $val,
  745. $query
  746. );
  747. $query = str_ireplace($field . ' ' . $oper, $field . ' ' . $val, $query);
  748. // check for integers to cast.
  749. $query = preg_replace('/\s+LIKE\s*(\d+)/i', " {$val} cast($1 as varchar(max))", $query);
  750. }
  751. }
  752. }
  753. }
  754. }
  755. }
  756. return $query;
  757. }
  758. /**
  759. * General create/alter query translations
  760. *
  761. * @since 2.7.1
  762. *
  763. * @param string $query Query coming in
  764. *
  765. * @return string Translated Query
  766. */
  767. function translate_create_queries($query)
  768. {
  769. if ( stripos($query, 'CREATE') !== 0 && stripos($query, 'ALTER') !== 0 ) {
  770. return $query;
  771. }
  772. // deal with alters in a bit
  773. if (stripos($query, 'ALTER') === 0) {
  774. return $query;
  775. }
  776. // fix enum as it doesn't exist in T-SQL
  777. if (stripos($query, 'enum(') !== false) {
  778. $enums = array_reverse($this->stripos_all($query, 'enum('));
  779. foreach ($enums as $start_pos) {
  780. $end = $this->get_matching_paren($query, $start_pos + 5);
  781. // get values inside enum
  782. $values = substr($query, $start_pos + 5, ($end - ($start_pos + 5)));
  783. $values = explode(',', $values);
  784. $all_int = true;
  785. foreach ($values as $value) {
  786. $val = trim(str_replace("'", '', $value));
  787. if (!is_numeric($val) || (int) $val != $val) {
  788. $all_int = false;
  789. }
  790. }
  791. // if enum of ints create an appropriate int column otherwise create a varchar
  792. if ($all_int) {
  793. $query = substr_replace($query, 'smallint', $start_pos, ($end + 1) - $start_pos);
  794. } else {
  795. $query = substr_replace($query, 'varchar(255)', $start_pos, ($end + 1) - $start_pos);
  796. }
  797. }
  798. }
  799. // remove IF NOT EXISTS as that doesn't exist in T-SQL
  800. $query = str_ireplace(' IF NOT EXISTS', '', $query);
  801. //save array to file_maps
  802. $this->fields_map->update_for($query);
  803. // change auto increment to indentity
  804. $start_positions = array_reverse($this->stripos_all($query, 'auto_increment'));
  805. if( stripos($query, 'auto_increment') > 0 ) {
  806. foreach ($start_positions as $start_pos) {
  807. $query = substr_replace($query, 'IDENTITY(1,1)', $start_pos, 14);
  808. }
  809. }
  810. if(stripos($query, 'AFTER') > 0) {
  811. $start_pos = stripos($query, 'AFTER');
  812. $query = substr($query, 0, $start_pos);
  813. }
  814. // replacement of certain data types and functions
  815. $fields = array(
  816. 'int (',
  817. 'int(',
  818. 'index (',
  819. 'index(',
  820. );
  821. foreach ( $fields as $field ) {
  822. // reverse so that when we make changes it wont effect the next change.
  823. $start_positions = array_reverse($this->stripos_all($query, $field));
  824. foreach ($start_positions as $start_pos) {
  825. $first_paren = stripos($query, '(', $start_pos);
  826. $end_pos = $this->get_matching_paren($query, $first_paren + 1) + 1;
  827. if( $field == 'index(' || $field == 'index (' ) {
  828. $query = substr_replace($query, '', $start_pos, $end_pos - $start_pos);
  829. } else {
  830. $query = substr_replace($query, rtrim(rtrim($field,'('), ' '), $start_pos, ($end_pos - $start_pos));
  831. }
  832. }
  833. }
  834. $query = str_ireplace("'0000-00-00 00:00:00'", 'getdate()', $query);
  835. // strip unsigned
  836. $query = str_ireplace("unsigned ", '', $query);
  837. // strip collation, engine type, etc from end of query
  838. $pos = stripos($query, '(', stripos($query, 'TABLE '));
  839. $end = $this->get_matching_paren($query, $pos + 1);
  840. $query = substr_replace($query, ');', $end);
  841. $query = str_ireplace("DEFAULT CHARACTER SET utf8", '', $query);
  842. $query = str_ireplace(" CHARACTER SET utf8", '', $query);
  843. // add collation
  844. $ac_types = array('tinytext', 'longtext', 'mediumtext', 'varchar');
  845. foreach ($ac_types as $ac_type) {
  846. $start_positions = array_reverse($this->stripos_all($query, $ac_type));
  847. foreach ($start_positions as $start_pos) {
  848. if ($ac_type == 'varchar') {
  849. $end = $this->get_matching_paren($query, $start_pos + (strlen($ac_type) + 1));
  850. $sub = substr($query, $end + 2, 7);
  851. $end_pos = $end + 1;
  852. } else {
  853. $query = substr_replace($query, 'TEXT', $start_pos, strlen($ac_type));
  854. $sub = substr($query, $start_pos + 5, 7);
  855. $end_pos = $start_pos + 4;
  856. }
  857. if ($sub !== 'COLLATE') {
  858. $query = $this->add_collation($query, $end_pos);
  859. }
  860. }
  861. }
  862. $keys = array();
  863. $table_pos = stripos($query, ' TABLE ') + 6;
  864. $table = substr($query, $table_pos, stripos($query, '(', $table_pos) - $table_pos);
  865. $table = trim($table);
  866. $reserved_words = array('public');
  867. // get column names to check for reserved words to encapsulate with [ ]
  868. foreach($this->fields_map->read() as $table_name => $table_fields) {
  869. if ($table_name == $table && is_array($table_fields)) {
  870. foreach ($table_fields as $field => $field_meta) {
  871. if (in_array($field, $reserved_words)) {
  872. $query = str_ireplace($field, "[{$field}]", $query);
  873. }
  874. }
  875. }
  876. }
  877. // get primary key constraints
  878. if ( stripos($query, 'PRIMARY KEY') > 0) {
  879. $start_positions = $this->stripos_all($query, 'PRIMARY KEY');
  880. foreach ($start_positions as $start_pos) {
  881. $start = stripos($query, '(', $start_pos);
  882. $end_paren = $this->get_matching_paren($query, $start + 1);
  883. $field = explode(',', substr($query, $start + 1, $end_paren - ($start + 1)));
  884. foreach ($field as $k => $v) {
  885. if (stripos($v, '(') !== false) {
  886. $field[$k] = preg_replace('/\(.*\)/', '', $v);
  887. }
  888. }
  889. $keys[] = array('type' => 'PRIMARY KEY', 'pos' => $start_pos, 'field' => $field);
  890. }
  891. }
  892. // get unique key constraints
  893. if ( stripos($query, 'UNIQUE KEY') > 0) {
  894. $start_positions = $this->stripos_all($query, 'UNIQUE KEY');
  895. foreach ($start_positions as $start_pos) {
  896. $start = stripos($query, '(', $start_pos);
  897. $end_paren = $this->get_matching_paren($query, $start + 1);
  898. $field = explode(',', substr($query, $start + 1, $end_paren - ($start + 1)));
  899. foreach ($field as $k => $v) {
  900. if (stripos($v, '(') !== false) {
  901. $field[$k] = preg_replace('/\(.*\)/', '', $v);
  902. }
  903. }
  904. $keys[] = array('type' => 'UNIQUE KEY', 'pos' => $start_pos, 'field' => $field);
  905. }
  906. }
  907. // get key constraints
  908. if ( stripos($query, 'KEY') > 0) {
  909. $start_positions = $this->stripos_all($query, 'KEY');
  910. foreach ($start_positions as $start_pos) {
  911. if (substr($query, $start_pos - 7, 6) !== 'UNIQUE'
  912. && substr($query, $start_pos - 8, 7) !== 'PRIMARY'
  913. && (substr($query, $start_pos - 1, 1) == ' ' || substr($query, $start_pos - 1, 1) == "\n")) {
  914. $start = stripos($query, '(', $start_pos);
  915. $end_paren = $this->get_matching_paren($query, $start + 1);
  916. $field = explode(',', substr($query, $start + 1, $end_paren - ($start + 1)));
  917. foreach ($field as $k => $v) {
  918. if (stripos($v, '(') !== false) {
  919. $field[$k] = preg_replace('/\(.*\)/', '', $v);
  920. }
  921. }
  922. $keys[] = array('type' => 'KEY', 'pos' => $start_pos, 'field' => $field);
  923. }
  924. }
  925. }
  926. $count = count($keys);
  927. $add_primary = false;
  928. $key_str = '';
  929. $lowest_start_pos = false;
  930. $unwanted = array(
  931. 'slug',
  932. 'name',
  933. 'term_id',
  934. 'taxonomy',
  935. 'term_taxonomy_id',
  936. 'comment_approved',
  937. 'comment_post_ID',
  938. 'comment_approved',
  939. 'link_visible',
  940. 'post_id',
  941. 'meta_key',
  942. 'post_type',
  943. 'post_status',
  944. 'post_date',
  945. 'ID',
  946. 'post_name',
  947. 'post_parent',
  948. 'user_login',
  949. 'user_nicename',
  950. 'user_id',
  951. );
  952. for ($i = 0; $i < $count; $i++) {
  953. if ($keys[$i]['pos'] < $lowest_start_pos || $lowest_start_pos === false) {
  954. $lowest_start_pos = $keys[$i]['pos'];
  955. }
  956. if ($keys[$i]['type'] == 'PRIMARY KEY') {
  957. $add_primary = true;
  958. }
  959. switch ($keys[$i]['type']) {
  960. case 'PRIMARY KEY':
  961. $str = "CONSTRAINT [" . $table . "_" . implode('_', $keys[$i]['field']) . "] PRIMARY KEY CLUSTERED (" . implode(',', $keys[$i]['field']) . ") WITH (IGNORE_DUP_KEY = OFF)";
  962. if (!$this->azure ) {
  963. $str .= " ON [PRIMARY]";
  964. }
  965. break;
  966. case 'UNIQUE KEY':
  967. $check = true;
  968. foreach ($keys[$i]['field'] as $field) {
  969. if (in_array($field, $unwanted)) {
  970. $check = false;
  971. }
  972. }
  973. if ($check) {
  974. if ($this->azure) {
  975. $str = 'CONSTRAINT [' . $table . '_' . implode('_', $keys[$i]['field']) . '] UNIQUE NONCLUSTERED (' . implode(',', $keys[$i]['field']) . ')';
  976. } else {
  977. $str = 'CONSTRAINT [' . $table . '_' . implode('_', $keys[$i]['field']) . '] UNIQUE NONCLUSTERED (' . implode(',', $keys[$i]['field']) . ')';
  978. }
  979. } else {
  980. $str = '';
  981. }
  982. break;
  983. case 'KEY':
  984. // CREATE NONCLUSTERED INDEX index_name ON table(col1,col2)
  985. $check = true;
  986. $str = '';
  987. foreach ($keys[$i]['field'] as $field) {
  988. if (in_array($field, $unwanted)) {
  989. $check = false;
  990. }
  991. }
  992. if ($check) {
  993. if (!is_array($this->following_query) && $this->following_query === false) {
  994. $this->following_query = array();
  995. } elseif (!is_array($this->following_query)) {
  996. $this->following_query = array($this->following_query);
  997. }
  998. if ($this->azure) {
  999. $this->following_query[] = 'CREATE CLUSTERED INDEX ' .
  1000. $table . '_' . implode('_', $keys[$i]['field']) .
  1001. ' ON '.$table.'('.implode(',', $keys[$i]['field']).')';
  1002. } else {
  1003. $this->following_query[] = 'CREATE NONCLUSTERED INDEX ' .
  1004. $table . '_' . implode('_', $keys[$i]['field']) .
  1005. ' ON '.$table.'('.implode(',', $keys[$i]['field']).')';
  1006. }
  1007. }
  1008. break;
  1009. }
  1010. if ($i !== $count - 1 && $str !== '') {
  1011. $str .= ',';
  1012. }
  1013. $key_str .= $str . "\n";
  1014. }
  1015. if ($key_str !== '') {
  1016. if ($add_primary && !$this->azure) {
  1017. $query = substr_replace($query, $key_str . ") ON [PRIMARY];", $lowest_start_pos);
  1018. } else {
  1019. $query = substr_replace($query, $key_str . ");", $lowest_start_pos);
  1020. }
  1021. }
  1022. return $query;
  1023. }
  1024. /**
  1025. * Given a first parenthesis ( ...will find its matching closing paren )
  1026. *
  1027. * @since 2.7.1
  1028. *
  1029. * @param string $str given string
  1030. * @param int $start_pos position of where desired starting paren begins+1
  1031. *
  1032. * @return int position of matching ending parenthesis
  1033. */
  1034. function get_matching_paren($str, $start_pos)
  1035. {
  1036. $count = strlen($str);
  1037. $bracket = 1;
  1038. for ( $i = $start_pos; $i < $count; $i++ ) {
  1039. if ( $str[$i] == '(' ) {
  1040. $bracket++;
  1041. } elseif ( $str[$i] == ')' ) {
  1042. $bracket--;
  1043. }
  1044. if ( $bracket == 0 ) {
  1045. return $i;
  1046. }
  1047. }
  1048. }
  1049. /**
  1050. * Get the Aliases in a query
  1051. * E.G. Field1 AS yyear, Field2 AS mmonth
  1052. * will return array with yyear and mmonth
  1053. *
  1054. * @since 2.7.1
  1055. *
  1056. * @param string $str a query
  1057. *
  1058. * @return array array of aliases in a query
  1059. */
  1060. function get_as_fields($query)
  1061. {
  1062. $arr = array();
  1063. $tok = preg_split('/[\s,]+/', $query);
  1064. $count = count($tok);
  1065. for ( $i = 0; $i < $count; $i++ ) {
  1066. if ( strtolower($tok[$i]) === 'as' ) {
  1067. $arr[] = $tok[($i + 1)];
  1068. }
  1069. }
  1070. return $arr;
  1071. }
  1072. /**
  1073. * Fix for SQL Server returning null values with one space.
  1074. * Fix for SQL Server returning datetime fields with milliseconds.
  1075. * Fix for SQL Server returning integer fields as integer (mysql returns as string)
  1076. *
  1077. * @since 2.7.1
  1078. *
  1079. * @param array $result_set result set array of an executed query
  1080. *
  1081. * @return array result set array with modified fields
  1082. */
  1083. function fix_results($result_set)
  1084. {
  1085. // If empty bail early.
  1086. if ( is_null($result_set)) {
  1087. return false;
  1088. }
  1089. if (is_array($result_set) && empty($result_set)) {
  1090. return array();
  1091. }
  1092. $map_fields = $this->fields_map->by_type('date');
  1093. $fields = array_keys(get_object_vars(current($result_set)));
  1094. foreach ( $result_set as $key => $result ) {
  1095. // Remove milliseconds
  1096. foreach ( $map_fields as $date_field ) {
  1097. if ( isset($result->$date_field) ) {
  1098. // date_format is a PHP5 function. sqlsrv is only PHP5 compat
  1099. // the result set for datetime columns is a PHP DateTime object, to extract
  1100. // the string we need to use date_format().
  1101. if (is_object($result->$date_field)) {
  1102. $result_set[$key]->$date_field = date_format($result->$date_field, 'Y-m-d H:i:s');
  1103. }
  1104. }
  1105. }
  1106. // Check for null values being returned as space and change integers to strings (to mimic mysql results)
  1107. foreach ( $fields as $field ) {
  1108. if ($field == 'crdate' || $field == 'refdate') {
  1109. $result_set[$key]->$field = date_format($result->$field, 'Y-m-d H:i:s');
  1110. }
  1111. if ( $result->$field === ' ' ) {
  1112. $result->$field = '';
  1113. }
  1114. if ( is_int($result->$field) ) {
  1115. $result->$field = (string) $result->$field;
  1116. }
  1117. }
  1118. }
  1119. return $result_set;
  1120. }
  1121. /**
  1122. * Check to see if INSERT has an ON DUPLICATE KEY statement
  1123. * This is MySQL specific and will be removed and put into
  1124. * a following_query UPDATE STATEMENT
  1125. *
  1126. * @param string $query Query coming in
  1127. * @return string query without ON DUPLICATE KEY statement
  1128. */
  1129. function on_duplicate_key($query)
  1130. {
  1131. if ( stripos($query, 'ON DUPLICATE KEY UPDATE') > 0 ) {
  1132. $table = substr($query, 12, (strpos($query, ' ', 12) - 12));
  1133. // currently just deal with wp_options table
  1134. if (stristr($table, 'options') !== FALSE) {
  1135. $start_pos = stripos($query, 'ON DUPLICATE KEY UPDATE');
  1136. $query = substr_replace($query, '', $start_pos);
  1137. $values_pos = stripos($query, 'VALUES');
  1138. $first_paren = stripos($query, '(', $values_pos);
  1139. $last_paren = $this->get_matching_paren($query, $first_paren + 1);
  1140. $values = explode(',', substr($query, ($first_paren + 1), ($last_paren-($first_paren + 1))));
  1141. // change this to use mapped fields
  1142. $update = 'UPDATE ' . $table . ' SET option_value = ' . $values[1] . ', autoload = ' . $values[2] .
  1143. ' WHERE option_name = ' . $values[0];
  1144. $this->following_query = $update;
  1145. }
  1146. }
  1147. return $query;
  1148. }
  1149. /**
  1150. * Check to see if an INSERT query has multiple VALUES blocks. If so we need create
  1151. * seperate queries for each.
  1152. * @since 2.7.1
  1153. *
  1154. * @param string $query Query coming in
  1155. *
  1156. * @return array array of insert queries
  1157. */
  1158. function split_insert_values($query)
  1159. {
  1160. $arr = array();
  1161. if (stripos($query, 'INSERT') === 0) {
  1162. $first = substr($query, 0, (stripos($query, 'VALUES') + 7));
  1163. $values = substr($query, (stripos($query, 'VALUES') + 7));
  1164. $arr = preg_split('/\),\s+\(/', $values);
  1165. foreach ($arr as $k => $v) {
  1166. if (substr($v, -1) !== ')') {
  1167. $v = $v . ')';
  1168. }
  1169. if (substr($v, 0, 1) !== '(') {
  1170. $v = '(' . $v;
  1171. }
  1172. $arr[$k] = $first . $v;
  1173. }
  1174. }
  1175. if (count($arr) < 2) {
  1176. return $query;
  1177. }
  1178. return $arr;
  1179. }
  1180. /**
  1181. * Check query to make sure translations weren't made to INSERT query values
  1182. * If so replace translation with original data.
  1183. * E.G. INSERT INTO wp_posts (wp_title) VALUES ('SELECT * FROM wp_posts LIMIT 1');
  1184. * The translations may change the value data to SELECT TOP 1 FROM wp_posts...in this case
  1185. * we don't want that to happen.
  1186. *
  1187. * @since 2.7.1
  1188. *
  1189. * @param string $query Query coming in
  1190. *
  1191. * @return string Verified Query
  1192. */
  1193. function verify_insert($query)
  1194. {
  1195. $values_pos = stripos($query, 'VALUES');
  1196. $first_paren = stripos($query, '(', $values_pos);
  1197. $last_paren = $this->get_matching_paren($query, $first_paren + 1);
  1198. $values = explode(',', substr($query, ($first_paren + 1), ($last_paren-($first_paren + 1))));
  1199. if ( count($this->prepare_args) !== count($values) ) {
  1200. return $query;
  1201. }
  1202. $i = 0;
  1203. foreach ( $values as $k => $value ) {
  1204. $value = trim($value);
  1205. foreach ($this->prepare_args as $r => $arg) {
  1206. if ( $k == $i && $arg !== $value ) {
  1207. if ( $arg !== '' && $arg !== '0000-00-00 00:00:00' ) {
  1208. $values[$k] = "'" . $arg . "'";
  1209. }
  1210. }
  1211. $i++;
  1212. }
  1213. }
  1214. $str = implode(',', $values);
  1215. $query = substr_replace($query, $str, ($first_paren + 1), ($last_paren - ($first_paren + 1)));
  1216. return $query;
  1217. }
  1218. /**
  1219. * Check query to make sure translations weren't made to UPDATE query values
  1220. * If so replace translation with original data.
  1221. * E.G. UPDATE wp_posts SET post_title = 'SELECT * FROM wp_posts LIMIT 1' WHERE post_id = 1;
  1222. * The translations may change the value data to SELECT TOP 1 FROM wp_posts...in this case
  1223. * we don't want that to happen
  1224. *
  1225. * @since 2.7.1
  1226. *
  1227. * @param string $query Query coming in
  1228. *
  1229. * @return string Verified Query
  1230. */
  1231. function verify_update($query)
  1232. {
  1233. if(empty($this->update_data)) {
  1234. return $query;
  1235. }
  1236. $values = array();
  1237. $keys = array_keys($this->update_data);
  1238. $start = stripos($query, 'SET') + 3;
  1239. $end = strripos($query, 'WHERE');
  1240. $sub = substr($query, $start, $end - $start);
  1241. $arr = explode(', ', $sub);
  1242. foreach ( $arr as $k => $v ) {
  1243. $v = trim($v);
  1244. $st = stripos($v, ' =');
  1245. $sv = substr($v, 0, $st);
  1246. if ( in_array($sv, $keys) ) {
  1247. $sp = substr($v, $st + 4, -1);
  1248. $values[] = str_replace("'", "''", $sp);
  1249. }
  1250. }
  1251. $update_data = array_values($this->update_data);
  1252. if ( count($update_data) == count($values) ) {
  1253. foreach ( $update_data as $key => $val ) {
  1254. if ( $update_data[$key] !== $values[$key] ) {
  1255. $values[$key] = str_replace("''", "'", $update_data[$key]);
  1256. }
  1257. }
  1258. foreach ( $values as $y => $vt ) {
  1259. $values[$y] = $keys[$y] . " = '" . $vt . "'";
  1260. }
  1261. $str = implode(', ', $values) . ' ';
  1262. $query = substr_replace($query, $str, ($start+1), ($end-($start+1)));
  1263. }
  1264. return $query;
  1265. }
  1266. /**
  1267. * Add collation for a field definition within a CREATE/ALTER query
  1268. *
  1269. * @since 2.8
  1270. * @param $type
  1271. *
  1272. * @return string
  1273. */
  1274. function add_collation($query, $pos)
  1275. {
  1276. $query = substr_replace($query, ' COLLATE Latin1_General_BIN', $pos, 0);
  1277. return $query;
  1278. }
  1279. /**
  1280. * Describe wrapper
  1281. *
  1282. * @since 2.8.5
  1283. * @param $table
  1284. *
  1285. * @return string
  1286. */
  1287. function describe($table)
  1288. {
  1289. $sql = "SELECT
  1290. c.name AS Field
  1291. ,t.name + t.length_string AS Type
  1292. ,CASE c.is_nullable WHEN 1 THEN 'YES' ELSE 'NO' END AS [Null]
  1293. ,CASE
  1294. WHEN EXISTS (SELECT * FROM sys.key_constraints AS kc
  1295. INNER JOIN sys.index_columns AS ic ON kc.unique_index_id = ic.index_id AND kc.parent_object_id = ic.object_id
  1296. WHERE kc.type = 'PK' AND ic.column_id = c.column_id AND c.object_id = ic.object_id)
  1297. THEN 'PRI'
  1298. WHEN EXISTS (SELECT * FROM sys.key_constraints AS kc
  1299. INNER JOIN sys.index_columns AS ic ON kc.unique_index_id = ic.index_id AND kc.parent_object_id = ic.object_id
  1300. WHERE kc.type <> 'PK' AND ic.column_id = c.column_id AND c.object_id = ic.object_id)
  1301. THEN 'UNI'
  1302. ELSE ''
  1303. END AS [Key]
  1304. ,ISNULL((
  1305. SELECT TOP(1)
  1306. dc.definition
  1307. FROM sys.default_constraints AS dc
  1308. WHERE dc.parent_column_id = c.column_id AND c.object_id = dc.parent_object_id)
  1309. ,'') AS [Default]
  1310. ,CASE
  1311. WHEN EXISTS (
  1312. SELECT
  1313. *
  1314. FROM sys.identity_columns AS ic
  1315. WHERE ic.column_id = c.column_id AND c.object_id = ic.object_id)
  1316. THEN 'auto_increment'
  1317. ELSE ''
  1318. END AS Extra
  1319. FROM sys.columns AS c
  1320. CROSS APPLY (
  1321. SELECT
  1322. t.name AS n1
  1323. ,CASE
  1324. -- Types with length
  1325. WHEN c.max_length > 0 AND t.name IN ('varchar', 'char', 'varbinary', 'binary') THEN '(' + CAST(c.max_length AS VARCHAR) + ')'
  1326. WHEN c.max_length > 0 AND t.name IN ('nvarchar', 'nchar') THEN '(' + CAST(c.max_length/2 AS VARCHAR) + ')'
  1327. WHEN c.max_length < 0 AND t.name IN ('nvarchar', 'varchar', 'varbinary') THEN '(max)'
  1328. -- Types with precision & scale
  1329. WHEN t.name IN ('decimal', 'numeric') THEN '(' + CAST(c.precision AS VARCHAR) + ',' + CAST(c.scale AS VARCHAR) + ')'
  1330. -- Types with only precision
  1331. WHEN t.name IN ('float') THEN '(' + CAST(c.precision AS VARCHAR) + ')'
  1332. -- Types with only scale
  1333. WHEN t.name IN ('datetime2', 'time', 'datetimeoffset') THEN '(' + CAST(c.scale AS VARCHAR) + ')'
  1334. -- The rest take no arguments
  1335. ELSE ''
  1336. END AS length_string
  1337. ,*
  1338. FROM sys.types AS t
  1339. WHERE t.system_type_id = c.system_type_id AND t.system_type_id = t.user_type_id
  1340. ) AS t
  1341. WHERE object_id = OBJECT_ID('{$table}');";
  1342. return $sql;
  1343. }
  1344. /**
  1345. * Get all occurrences(positions) of a string within a string
  1346. *
  1347. * @since 2.8
  1348. * @param $type
  1349. *
  1350. * @return array
  1351. */
  1352. function stripos_all($haystack, $needle, $offset = 0)
  1353. {
  1354. $arr = array();
  1355. while ($offset !== false) {
  1356. $pos = stripos($haystack, $needle, $offset);
  1357. if ($pos !== false) {
  1358. $arr[] = $pos;
  1359. $pos = $pos + str

Large files files are truncated, but you can click here to view the full file