adodb-mssql_n.inc.php 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php
  2. /// $Id $
  3. ///////////////////////////////////////////////////////////////////////////
  4. // //
  5. // NOTICE OF COPYRIGHT //
  6. // //
  7. // ADOdb - Database Abstraction Library for PHP //
  8. // http://adodb.sourceforge.net/ //
  9. // //
  10. // Copyright (c) 2000-2014 John Lim (jlim\@natsoft.com.my) //
  11. // All rights reserved. //
  12. // Released under both BSD license and LGPL library license. //
  13. // Whenever there is any discrepancy between the two licenses, //
  14. // the BSD license will take precedence //
  15. // //
  16. // Moodle - Modular Object-Oriented Dynamic Learning Environment //
  17. // http://moodle.com //
  18. // //
  19. // Copyright (C) 2001-3001 Martin Dougiamas http://dougiamas.com //
  20. // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
  21. // //
  22. // This program is free software; you can redistribute it and/or modify //
  23. // it under the terms of the GNU General Public License as published by //
  24. // the Free Software Foundation; either version 2 of the License, or //
  25. // (at your option) any later version. //
  26. // //
  27. // This program is distributed in the hope that it will be useful, //
  28. // but WITHOUT ANY WARRANTY; without even the implied warranty of //
  29. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
  30. // GNU General Public License for more details: //
  31. // //
  32. // http://www.gnu.org/copyleft/gpl.html //
  33. // //
  34. ///////////////////////////////////////////////////////////////////////////
  35. /**
  36. * MSSQL Driver with auto-prepended "N" for correct unicode storage
  37. * of SQL literal strings. Intended to be used with MSSQL drivers that
  38. * are sending UCS-2 data to MSSQL (FreeTDS and ODBTP) in order to get
  39. * true cross-db compatibility from the application point of view.
  40. */
  41. // security - hide paths
  42. if (!defined('ADODB_DIR')) die();
  43. // one useful constant
  44. if (!defined('SINGLEQUOTE')) define('SINGLEQUOTE', "'");
  45. include_once(ADODB_DIR.'/drivers/adodb-mssql.inc.php');
  46. class ADODB_mssql_n extends ADODB_mssql {
  47. var $databaseType = "mssql_n";
  48. function _query($sql,$inputarr=false)
  49. {
  50. $sql = $this->_appendN($sql);
  51. return ADODB_mssql::_query($sql,$inputarr);
  52. }
  53. /**
  54. * This function will intercept all the literals used in the SQL, prepending the "N" char to them
  55. * in order to allow mssql to store properly data sent in the correct UCS-2 encoding (by freeTDS
  56. * and ODBTP) keeping SQL compatibility at ADOdb level (instead of hacking every project to add
  57. * the "N" notation when working against MSSQL.
  58. *
  59. * The orginal note indicated that this hack should only be used if ALL the char-based columns
  60. * in your DB are of type nchar, nvarchar and ntext, but testing seems to indicate that SQL server
  61. * doesn't seem to care if the statement is used against char etc fields.
  62. *
  63. * @todo This function should raise an ADOdb error if one of the transformations fail
  64. *
  65. * @param mixed $inboundData Either a string containing an SQL statement
  66. * or an array with resources from prepared statements
  67. *
  68. * @return mixed
  69. */
  70. function _appendN($inboundData) {
  71. $inboundIsArray = false;
  72. if (is_array($inboundData))
  73. {
  74. $inboundIsArray = true;
  75. $inboundArray = $inboundData;
  76. } else
  77. $inboundArray = (array)$inboundData;
  78. /*
  79. * All changes will be placed here
  80. */
  81. $outboundArray = $inboundArray;
  82. foreach($inboundArray as $inboundKey=>$inboundValue)
  83. {
  84. if (is_resource($inboundValue))
  85. {
  86. /*
  87. * Prepared statement resource
  88. */
  89. if ($this->debug)
  90. ADOConnection::outp("{$this->databaseType} index $inboundKey value is resource, continue");
  91. continue;
  92. }
  93. if (strpos($inboundValue, SINGLEQUOTE) === false)
  94. {
  95. /*
  96. * Check we have something to manipulate
  97. */
  98. if ($this->debug)
  99. ADOConnection::outp("{$this->databaseType} index $inboundKey value $inboundValue has no single quotes, continue");
  100. continue;
  101. }
  102. /*
  103. * Check we haven't an odd number of single quotes (this can cause problems below
  104. * and should be considered one wrong SQL). Exit with debug info.
  105. */
  106. if ((substr_count($inboundValue, SINGLEQUOTE) & 1))
  107. {
  108. if ($this->debug)
  109. ADOConnection::outp("{$this->databaseType} internal transformation: not converted. Wrong number of quotes (odd)");
  110. break;
  111. }
  112. /*
  113. * Check we haven't any backslash + single quote combination. It should mean wrong
  114. * backslashes use (bad magic_quotes_sybase?). Exit with debug info.
  115. */
  116. $regexp = '/(\\\\' . SINGLEQUOTE . '[^' . SINGLEQUOTE . '])/';
  117. if (preg_match($regexp, $inboundValue))
  118. {
  119. if ($this->debug)
  120. ADOConnection::outp("{$this->databaseType} internal transformation: not converted. Found bad use of backslash + single quote");
  121. break;
  122. }
  123. /*
  124. * Remove pairs of single-quotes
  125. */
  126. $pairs = array();
  127. $regexp = '/(' . SINGLEQUOTE . SINGLEQUOTE . ')/';
  128. preg_match_all($regexp, $inboundValue, $list_of_pairs);
  129. if ($list_of_pairs)
  130. {
  131. foreach (array_unique($list_of_pairs[0]) as $key=>$value)
  132. $pairs['<@#@#@PAIR-'.$key.'@#@#@>'] = $value;
  133. if (!empty($pairs))
  134. $inboundValue = str_replace($pairs, array_keys($pairs), $inboundValue);
  135. }
  136. /*
  137. * Remove the rest of literals present in the query
  138. */
  139. $literals = array();
  140. $regexp = '/(N?' . SINGLEQUOTE . '.*?' . SINGLEQUOTE . ')/is';
  141. preg_match_all($regexp, $inboundValue, $list_of_literals);
  142. if ($list_of_literals)
  143. {
  144. foreach (array_unique($list_of_literals[0]) as $key=>$value)
  145. $literals['<#@#@#LITERAL-'.$key.'#@#@#>'] = $value;
  146. if (!empty($literals))
  147. $inboundValue = str_replace($literals, array_keys($literals), $inboundValue);
  148. }
  149. /*
  150. * Analyse literals to prepend the N char to them if their contents aren't numeric
  151. */
  152. if (!empty($literals))
  153. {
  154. foreach ($literals as $key=>$value) {
  155. if (!is_numeric(trim($value, SINGLEQUOTE)))
  156. /*
  157. * Non numeric string, prepend our dear N, whilst
  158. * Trimming potentially existing previous "N"
  159. */
  160. $literals[$key] = 'N' . trim($value, 'N');
  161. }
  162. }
  163. /*
  164. * Re-apply literals to the text
  165. */
  166. if (!empty($literals))
  167. $inboundValue = str_replace(array_keys($literals), $literals, $inboundValue);
  168. /*
  169. * Any pairs followed by N' must be switched to N' followed by those pairs
  170. * (or strings beginning with single quotes will fail)
  171. */
  172. $inboundValue = preg_replace("/((<@#@#@PAIR-(\d+)@#@#@>)+)N'/", "N'$1", $inboundValue);
  173. /*
  174. * Re-apply pairs of single-quotes to the text
  175. */
  176. if (!empty($pairs))
  177. $inboundValue = str_replace(array_keys($pairs), $pairs, $inboundValue);
  178. /*
  179. * Print transformation if debug = on
  180. */
  181. if (strcmp($inboundValue,$inboundArray[$inboundKey]) <> 0 && $this->debug)
  182. ADOConnection::outp("{$this->databaseType} internal transformation: {$inboundArray[$inboundKey]} to {$inboundValue}");
  183. if (strcmp($inboundValue,$inboundArray[$inboundKey]) <> 0)
  184. /*
  185. * Place the transformed value into the outbound array
  186. */
  187. $outboundArray[$inboundKey] = $inboundValue;
  188. }
  189. /*
  190. * Any transformations are in the $outboundArray
  191. */
  192. if ($inboundIsArray)
  193. return $outboundArray;
  194. /*
  195. * We passed a string in originally
  196. */
  197. return $outboundArray[0];
  198. }
  199. }
  200. class ADORecordset_mssql_n extends ADORecordset_mssql {
  201. var $databaseType = "mssql_n";
  202. function __construct($id,$mode=false)
  203. {
  204. parent::__construct($id,$mode);
  205. }
  206. }