adodb-mssql.inc.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  1. <?php
  2. /*
  3. @version v5.20.17 31-Mar-2020
  4. @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  5. @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
  6. Released under both BSD license and Lesser GPL library license.
  7. Whenever there is any discrepancy between the two licenses,
  8. the BSD license will take precedence.
  9. Set tabs to 4 for best viewing.
  10. Latest version is available at http://adodb.org/
  11. Native mssql driver. Requires mssql client. Works on Windows.
  12. To configure for Unix, see
  13. http://phpbuilder.com/columns/alberto20000919.php3
  14. */
  15. // security - hide paths
  16. if (!defined('ADODB_DIR')) die();
  17. //----------------------------------------------------------------
  18. // MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
  19. // and this causes tons of problems because localized versions of
  20. // MSSQL will return the dates in dmy or mdy order; and also the
  21. // month strings depends on what language has been configured. The
  22. // following two variables allow you to control the localization
  23. // settings - Ugh.
  24. //
  25. // MORE LOCALIZATION INFO
  26. // ----------------------
  27. // To configure datetime, look for and modify sqlcommn.loc,
  28. // typically found in c:\mssql\install
  29. // Also read :
  30. // http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
  31. // Alternatively use:
  32. // CONVERT(char(12),datecol,120)
  33. //----------------------------------------------------------------
  34. // has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
  35. if (ADODB_PHPVER >= 0x4300) {
  36. // docs say 4.2.0, but testing shows only since 4.3.0 does it work!
  37. ini_set('mssql.datetimeconvert',0);
  38. } else {
  39. global $ADODB_mssql_mths; // array, months must be upper-case
  40. $ADODB_mssql_date_order = 'mdy';
  41. $ADODB_mssql_mths = array(
  42. 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
  43. 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
  44. }
  45. //---------------------------------------------------------------------------
  46. // Call this to autoset $ADODB_mssql_date_order at the beginning of your code,
  47. // just after you connect to the database. Supports mdy and dmy only.
  48. // Not required for PHP 4.2.0 and above.
  49. function AutoDetect_MSSQL_Date_Order($conn)
  50. {
  51. global $ADODB_mssql_date_order;
  52. $adate = $conn->GetOne('select getdate()');
  53. if ($adate) {
  54. $anum = (int) $adate;
  55. if ($anum > 0) {
  56. if ($anum > 31) {
  57. //ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently");
  58. } else
  59. $ADODB_mssql_date_order = 'dmy';
  60. } else
  61. $ADODB_mssql_date_order = 'mdy';
  62. }
  63. }
  64. class ADODB_mssql extends ADOConnection {
  65. var $databaseType = "mssql";
  66. var $dataProvider = "mssql";
  67. var $replaceQuote = "''"; // string to use to replace quotes
  68. var $fmtDate = "'Y-m-d'";
  69. var $fmtTimeStamp = "'Y-m-d\TH:i:s'";
  70. var $hasInsertID = true;
  71. var $substr = "substring";
  72. var $length = 'len';
  73. var $hasAffectedRows = true;
  74. var $metaDatabasesSQL = "select name from sysdatabases where name <> 'master'";
  75. var $metaTablesSQL="select name,case when type='U' then 'T' else 'V' end from sysobjects where (type='U' or type='V') and (name not in ('sysallocations','syscolumns','syscomments','sysdepends','sysfilegroups','sysfiles','sysfiles1','sysforeignkeys','sysfulltextcatalogs','sysindexes','sysindexkeys','sysmembers','sysobjects','syspermissions','sysprotects','sysreferences','systypes','sysusers','sysalternates','sysconstraints','syssegments','REFERENTIAL_CONSTRAINTS','CHECK_CONSTRAINTS','CONSTRAINT_TABLE_USAGE','CONSTRAINT_COLUMN_USAGE','VIEWS','VIEW_TABLE_USAGE','VIEW_COLUMN_USAGE','SCHEMATA','TABLES','TABLE_CONSTRAINTS','TABLE_PRIVILEGES','COLUMNS','COLUMN_DOMAIN_USAGE','COLUMN_PRIVILEGES','DOMAINS','DOMAIN_CONSTRAINTS','KEY_COLUMN_USAGE','dtproperties'))";
  76. var $metaColumnsSQL = # xtype==61 is datetime
  77. "select c.name,t.name,c.length,c.isnullable, c.status,
  78. (case when c.xusertype=61 then 0 else c.xprec end),
  79. (case when c.xusertype=61 then 0 else c.xscale end)
  80. from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'";
  81. var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE
  82. var $hasGenID = true;
  83. var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
  84. var $sysTimeStamp = 'GetDate()';
  85. var $_has_mssql_init;
  86. var $maxParameterLen = 4000;
  87. var $arrayClass = 'ADORecordSet_array_mssql';
  88. var $uniqueSort = true;
  89. var $leftOuter = '*=';
  90. var $rightOuter = '=*';
  91. var $ansiOuter = true; // for mssql7 or later
  92. var $poorAffectedRows = true;
  93. var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
  94. var $uniqueOrderBy = true;
  95. var $_bindInputArray = true;
  96. var $forceNewConnect = false;
  97. function __construct()
  98. {
  99. $this->_has_mssql_init = (strnatcmp(PHP_VERSION,'4.1.0')>=0);
  100. }
  101. function ServerInfo()
  102. {
  103. global $ADODB_FETCH_MODE;
  104. if ($this->fetchMode === false) {
  105. $savem = $ADODB_FETCH_MODE;
  106. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  107. } else
  108. $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
  109. if (0) {
  110. $stmt = $this->PrepareSP('sp_server_info');
  111. $val = 2;
  112. $this->Parameter($stmt,$val,'attribute_id');
  113. $row = $this->GetRow($stmt);
  114. }
  115. $row = $this->GetRow("execute sp_server_info 2");
  116. if ($this->fetchMode === false) {
  117. $ADODB_FETCH_MODE = $savem;
  118. } else
  119. $this->SetFetchMode($savem);
  120. $arr['description'] = $row[2];
  121. $arr['version'] = ADOConnection::_findvers($arr['description']);
  122. return $arr;
  123. }
  124. function IfNull( $field, $ifNull )
  125. {
  126. return " ISNULL($field, $ifNull) "; // if MS SQL Server
  127. }
  128. function _insertid()
  129. {
  130. // SCOPE_IDENTITY()
  131. // Returns the last IDENTITY value inserted into an IDENTITY column in
  132. // the same scope. A scope is a module -- a stored procedure, trigger,
  133. // function, or batch. Thus, two statements are in the same scope if
  134. // they are in the same stored procedure, function, or batch.
  135. if ($this->lastInsID !== false) {
  136. return $this->lastInsID; // InsID from sp_executesql call
  137. } else {
  138. return $this->GetOne($this->identitySQL);
  139. }
  140. }
  141. /**
  142. * Correctly quotes a string so that all strings are escaped. We prefix and append
  143. * to the string single-quotes.
  144. * An example is $db->qstr("Don't bother",magic_quotes_runtime());
  145. *
  146. * @param s the string to quote
  147. * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
  148. * This undoes the stupidity of magic quotes for GPC.
  149. *
  150. * @return quoted string to be sent back to database
  151. */
  152. function qstr($s,$magic_quotes=false)
  153. {
  154. if (!$magic_quotes) {
  155. return "'".str_replace("'",$this->replaceQuote,$s)."'";
  156. }
  157. // undo magic quotes for " unless sybase is on
  158. $sybase = ini_get('magic_quotes_sybase');
  159. if (!$sybase) {
  160. $s = str_replace('\\"','"',$s);
  161. if ($this->replaceQuote == "\\'") // ' already quoted, no need to change anything
  162. return "'$s'";
  163. else {// change \' to '' for sybase/mssql
  164. $s = str_replace('\\\\','\\',$s);
  165. return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
  166. }
  167. } else {
  168. return "'".$s."'";
  169. }
  170. }
  171. // moodle change end - see readme_moodle.txt
  172. function _affectedrows()
  173. {
  174. return $this->GetOne('select @@rowcount');
  175. }
  176. var $_dropSeqSQL = "drop table %s";
  177. function CreateSequence($seq='adodbseq',$start=1)
  178. {
  179. $this->Execute('BEGIN TRANSACTION adodbseq');
  180. $start -= 1;
  181. $this->Execute("create table $seq (id float(53))");
  182. $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
  183. if (!$ok) {
  184. $this->Execute('ROLLBACK TRANSACTION adodbseq');
  185. return false;
  186. }
  187. $this->Execute('COMMIT TRANSACTION adodbseq');
  188. return true;
  189. }
  190. function GenID($seq='adodbseq',$start=1)
  191. {
  192. //$this->debug=1;
  193. $this->Execute('BEGIN TRANSACTION adodbseq');
  194. $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
  195. if (!$ok) {
  196. $this->Execute("create table $seq (id float(53))");
  197. $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
  198. if (!$ok) {
  199. $this->Execute('ROLLBACK TRANSACTION adodbseq');
  200. return false;
  201. }
  202. $this->Execute('COMMIT TRANSACTION adodbseq');
  203. return $start;
  204. }
  205. $num = $this->GetOne("select id from $seq");
  206. $this->Execute('COMMIT TRANSACTION adodbseq');
  207. return $num;
  208. // in old implementation, pre 1.90, we returned GUID...
  209. //return $this->GetOne("SELECT CONVERT(varchar(255), NEWID()) AS 'Char'");
  210. }
  211. function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
  212. {
  213. $nrows = (int) $nrows;
  214. $offset = (int) $offset;
  215. if ($nrows > 0 && $offset <= 0) {
  216. $sql = preg_replace(
  217. '/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql);
  218. if ($secs2cache)
  219. $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
  220. else
  221. $rs = $this->Execute($sql,$inputarr);
  222. } else
  223. $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
  224. return $rs;
  225. }
  226. // Format date column in sql string given an input format that understands Y M D
  227. function SQLDate($fmt, $col=false)
  228. {
  229. if (!$col) $col = $this->sysTimeStamp;
  230. $s = '';
  231. $len = strlen($fmt);
  232. for ($i=0; $i < $len; $i++) {
  233. if ($s) $s .= '+';
  234. $ch = $fmt[$i];
  235. switch($ch) {
  236. case 'Y':
  237. case 'y':
  238. $s .= "datename(yyyy,$col)";
  239. break;
  240. case 'M':
  241. $s .= "convert(char(3),$col,0)";
  242. break;
  243. case 'm':
  244. $s .= "replace(str(month($col),2),' ','0')";
  245. break;
  246. case 'Q':
  247. case 'q':
  248. $s .= "datename(quarter,$col)";
  249. break;
  250. case 'D':
  251. case 'd':
  252. $s .= "replace(str(day($col),2),' ','0')";
  253. break;
  254. case 'h':
  255. $s .= "substring(convert(char(14),$col,0),13,2)";
  256. break;
  257. case 'H':
  258. $s .= "replace(str(datepart(hh,$col),2),' ','0')";
  259. break;
  260. case 'i':
  261. $s .= "replace(str(datepart(mi,$col),2),' ','0')";
  262. break;
  263. case 's':
  264. $s .= "replace(str(datepart(ss,$col),2),' ','0')";
  265. break;
  266. case 'a':
  267. case 'A':
  268. $s .= "substring(convert(char(19),$col,0),18,2)";
  269. break;
  270. default:
  271. if ($ch == '\\') {
  272. $i++;
  273. $ch = substr($fmt,$i,1);
  274. }
  275. $s .= $this->qstr($ch);
  276. break;
  277. }
  278. }
  279. return $s;
  280. }
  281. function BeginTrans()
  282. {
  283. if ($this->transOff) return true;
  284. $this->transCnt += 1;
  285. $ok = $this->Execute('BEGIN TRAN');
  286. return $ok;
  287. }
  288. function CommitTrans($ok=true)
  289. {
  290. if ($this->transOff) return true;
  291. if (!$ok) return $this->RollbackTrans();
  292. if ($this->transCnt) $this->transCnt -= 1;
  293. $ok = $this->Execute('COMMIT TRAN');
  294. return $ok;
  295. }
  296. function RollbackTrans()
  297. {
  298. if ($this->transOff) return true;
  299. if ($this->transCnt) $this->transCnt -= 1;
  300. $ok = $this->Execute('ROLLBACK TRAN');
  301. return $ok;
  302. }
  303. function SetTransactionMode( $transaction_mode )
  304. {
  305. $this->_transmode = $transaction_mode;
  306. if (empty($transaction_mode)) {
  307. $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
  308. return;
  309. }
  310. if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
  311. $this->Execute("SET TRANSACTION ".$transaction_mode);
  312. }
  313. /*
  314. Usage:
  315. $this->BeginTrans();
  316. $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
  317. # some operation on both tables table1 and table2
  318. $this->CommitTrans();
  319. See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
  320. */
  321. function RowLock($tables,$where,$col='1 as adodbignore')
  322. {
  323. if ($col == '1 as adodbignore') $col = 'top 1 null as ignore';
  324. if (!$this->transCnt) $this->BeginTrans();
  325. return $this->GetOne("select $col from $tables with (ROWLOCK,HOLDLOCK) where $where");
  326. }
  327. function MetaColumns($table, $normalize=true)
  328. {
  329. // $arr = ADOConnection::MetaColumns($table);
  330. // return $arr;
  331. $this->_findschema($table,$schema);
  332. if ($schema) {
  333. $dbName = $this->database;
  334. $this->SelectDB($schema);
  335. }
  336. global $ADODB_FETCH_MODE;
  337. $save = $ADODB_FETCH_MODE;
  338. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  339. if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
  340. $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
  341. if ($schema) {
  342. $this->SelectDB($dbName);
  343. }
  344. if (isset($savem)) $this->SetFetchMode($savem);
  345. $ADODB_FETCH_MODE = $save;
  346. if (!is_object($rs)) {
  347. $false = false;
  348. return $false;
  349. }
  350. $retarr = array();
  351. while (!$rs->EOF){
  352. $fld = new ADOFieldObject();
  353. $fld->name = $rs->fields[0];
  354. $fld->type = $rs->fields[1];
  355. $fld->not_null = (!$rs->fields[3]);
  356. $fld->auto_increment = ($rs->fields[4] == 128); // sys.syscolumns status field. 0x80 = 128 ref: http://msdn.microsoft.com/en-us/library/ms186816.aspx
  357. if (isset($rs->fields[5]) && $rs->fields[5]) {
  358. if ($rs->fields[5]>0) $fld->max_length = $rs->fields[5];
  359. $fld->scale = $rs->fields[6];
  360. if ($fld->scale>0) $fld->max_length += 1;
  361. } else
  362. $fld->max_length = $rs->fields[2];
  363. if ($save == ADODB_FETCH_NUM) {
  364. $retarr[] = $fld;
  365. } else {
  366. $retarr[strtoupper($fld->name)] = $fld;
  367. }
  368. $rs->MoveNext();
  369. }
  370. $rs->Close();
  371. return $retarr;
  372. }
  373. function MetaIndexes($table,$primary=false, $owner=false)
  374. {
  375. $table = $this->qstr($table);
  376. $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
  377. CASE WHEN I.indid BETWEEN 1 AND 254 AND (I.status & 2048 = 2048 OR I.Status = 16402 AND O.XType = 'V') THEN 1 ELSE 0 END AS IsPK,
  378. CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
  379. FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
  380. INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
  381. INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
  382. WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
  383. ORDER BY O.name, I.Name, K.keyno";
  384. global $ADODB_FETCH_MODE;
  385. $save = $ADODB_FETCH_MODE;
  386. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  387. if ($this->fetchMode !== FALSE) {
  388. $savem = $this->SetFetchMode(FALSE);
  389. }
  390. $rs = $this->Execute($sql);
  391. if (isset($savem)) {
  392. $this->SetFetchMode($savem);
  393. }
  394. $ADODB_FETCH_MODE = $save;
  395. if (!is_object($rs)) {
  396. return FALSE;
  397. }
  398. $indexes = array();
  399. while ($row = $rs->FetchRow()) {
  400. if ($primary && !$row[5]) continue;
  401. $indexes[$row[0]]['unique'] = $row[6];
  402. $indexes[$row[0]]['columns'][] = $row[1];
  403. }
  404. return $indexes;
  405. }
  406. function MetaForeignKeys($table, $owner=false, $upper=false)
  407. {
  408. global $ADODB_FETCH_MODE;
  409. $save = $ADODB_FETCH_MODE;
  410. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  411. $table = $this->qstr(strtoupper($table));
  412. $sql =
  413. "select object_name(constid) as constraint_name,
  414. col_name(fkeyid, fkey) as column_name,
  415. object_name(rkeyid) as referenced_table_name,
  416. col_name(rkeyid, rkey) as referenced_column_name
  417. from sysforeignkeys
  418. where upper(object_name(fkeyid)) = $table
  419. order by constraint_name, referenced_table_name, keyno";
  420. $constraints = $this->GetArray($sql);
  421. $ADODB_FETCH_MODE = $save;
  422. $arr = false;
  423. foreach($constraints as $constr) {
  424. //print_r($constr);
  425. $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
  426. }
  427. if (!$arr) return false;
  428. $arr2 = false;
  429. foreach($arr as $k => $v) {
  430. foreach($v as $a => $b) {
  431. if ($upper) $a = strtoupper($a);
  432. $arr2[$a] = $b;
  433. }
  434. }
  435. return $arr2;
  436. }
  437. //From: Fernando Moreira <FMoreira@imediata.pt>
  438. function MetaDatabases()
  439. {
  440. if(@mssql_select_db("master")) {
  441. $qry=$this->metaDatabasesSQL;
  442. if($rs=@mssql_query($qry,$this->_connectionID)){
  443. $tmpAr=$ar=array();
  444. while($tmpAr=@mssql_fetch_row($rs))
  445. $ar[]=$tmpAr[0];
  446. @mssql_select_db($this->database);
  447. if(sizeof($ar))
  448. return($ar);
  449. else
  450. return(false);
  451. } else {
  452. @mssql_select_db($this->database);
  453. return(false);
  454. }
  455. }
  456. return(false);
  457. }
  458. // "Stein-Aksel Basma" <basma@accelero.no>
  459. // tested with MSSQL 2000
  460. function MetaPrimaryKeys($table, $owner=false)
  461. {
  462. global $ADODB_FETCH_MODE;
  463. $schema = '';
  464. $this->_findschema($table,$schema);
  465. if (!$schema) $schema = $this->database;
  466. if ($schema) $schema = "and k.table_catalog like '$schema%'";
  467. $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
  468. information_schema.table_constraints tc
  469. where tc.constraint_name = k.constraint_name and tc.constraint_type =
  470. 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
  471. $savem = $ADODB_FETCH_MODE;
  472. $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
  473. $a = $this->GetCol($sql);
  474. $ADODB_FETCH_MODE = $savem;
  475. if ($a && sizeof($a)>0) return $a;
  476. $false = false;
  477. return $false;
  478. }
  479. function MetaTables($ttype=false,$showSchema=false,$mask=false)
  480. {
  481. if ($mask) {
  482. $save = $this->metaTablesSQL;
  483. $mask = $this->qstr(($mask));
  484. $this->metaTablesSQL .= " AND name like $mask";
  485. }
  486. $ret = ADOConnection::MetaTables($ttype,$showSchema);
  487. if ($mask) {
  488. $this->metaTablesSQL = $save;
  489. }
  490. return $ret;
  491. }
  492. function SelectDB($dbName)
  493. {
  494. $this->database = $dbName;
  495. $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
  496. if ($this->_connectionID) {
  497. return @mssql_select_db($dbName);
  498. }
  499. else return false;
  500. }
  501. function ErrorMsg()
  502. {
  503. if (empty($this->_errorMsg)){
  504. $this->_errorMsg = mssql_get_last_message();
  505. }
  506. return $this->_errorMsg;
  507. }
  508. function ErrorNo()
  509. {
  510. if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode;
  511. if (empty($this->_errorMsg)) {
  512. $this->_errorMsg = mssql_get_last_message();
  513. }
  514. $id = @mssql_query("select @@ERROR",$this->_connectionID);
  515. if (!$id) return false;
  516. $arr = mssql_fetch_array($id);
  517. @mssql_free_result($id);
  518. if (is_array($arr)) return $arr[0];
  519. else return -1;
  520. }
  521. // returns true or false, newconnect supported since php 5.1.0.
  522. function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$newconnect=false)
  523. {
  524. if (!function_exists('mssql_pconnect')) return null;
  525. $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword,$newconnect);
  526. if ($this->_connectionID === false) return false;
  527. if ($argDatabasename) return $this->SelectDB($argDatabasename);
  528. return true;
  529. }
  530. // returns true or false
  531. function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
  532. {
  533. if (!function_exists('mssql_pconnect')) return null;
  534. $this->_connectionID = mssql_pconnect($argHostname,$argUsername,$argPassword);
  535. if ($this->_connectionID === false) return false;
  536. // persistent connections can forget to rollback on crash, so we do it here.
  537. if ($this->autoRollback) {
  538. $cnt = $this->GetOne('select @@TRANCOUNT');
  539. while (--$cnt >= 0) $this->Execute('ROLLBACK TRAN');
  540. }
  541. if ($argDatabasename) return $this->SelectDB($argDatabasename);
  542. return true;
  543. }
  544. function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
  545. {
  546. return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true);
  547. }
  548. function Prepare($sql)
  549. {
  550. $sqlarr = explode('?',$sql);
  551. if (sizeof($sqlarr) <= 1) return $sql;
  552. $sql2 = $sqlarr[0];
  553. for ($i = 1, $max = sizeof($sqlarr); $i < $max; $i++) {
  554. $sql2 .= '@P'.($i-1) . $sqlarr[$i];
  555. }
  556. return array($sql,$this->qstr($sql2),$max,$sql2);
  557. }
  558. function PrepareSP($sql,$param=true)
  559. {
  560. if (!$this->_has_mssql_init) {
  561. ADOConnection::outp( "PrepareSP: mssql_init only available since PHP 4.1.0");
  562. return $sql;
  563. }
  564. $stmt = mssql_init($sql,$this->_connectionID);
  565. if (!$stmt) return $sql;
  566. return array($sql,$stmt);
  567. }
  568. // returns concatenated string
  569. // MSSQL requires integers to be cast as strings
  570. // automatically cast every datatype to VARCHAR(255)
  571. // @author David Rogers (introspectshun)
  572. function Concat()
  573. {
  574. $s = "";
  575. $arr = func_get_args();
  576. // Split single record on commas, if possible
  577. if (sizeof($arr) == 1) {
  578. foreach ($arr as $arg) {
  579. $args = explode(',', $arg);
  580. }
  581. $arr = $args;
  582. }
  583. array_walk(
  584. $arr,
  585. function(&$value, $key) {
  586. $value = "CAST(" . $value . " AS VARCHAR(255))";
  587. }
  588. );
  589. $s = implode('+',$arr);
  590. if (sizeof($arr) > 0) return "$s";
  591. return '';
  592. }
  593. /*
  594. Usage:
  595. $stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group
  596. # note that the parameter does not have @ in front!
  597. $db->Parameter($stmt,$id,'myid');
  598. $db->Parameter($stmt,$group,'group',false,64);
  599. $db->Execute($stmt);
  600. @param $stmt Statement returned by Prepare() or PrepareSP().
  601. @param $var PHP variable to bind to. Can set to null (for isNull support).
  602. @param $name Name of stored procedure variable name to bind to.
  603. @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
  604. @param [$maxLen] Holds an maximum length of the variable.
  605. @param [$type] The data type of $var. Legal values depend on driver.
  606. See mssql_bind documentation at php.net.
  607. */
  608. function Parameter(&$stmt, &$var, $name, $isOutput=false, $maxLen=4000, $type=false)
  609. {
  610. if (!$this->_has_mssql_init) {
  611. ADOConnection::outp( "Parameter: mssql_bind only available since PHP 4.1.0");
  612. return false;
  613. }
  614. $isNull = is_null($var); // php 4.0.4 and above...
  615. if ($type === false)
  616. switch(gettype($var)) {
  617. default:
  618. case 'string': $type = SQLVARCHAR; break;
  619. case 'double': $type = SQLFLT8; break;
  620. case 'integer': $type = SQLINT4; break;
  621. case 'boolean': $type = SQLINT1; break; # SQLBIT not supported in 4.1.0
  622. }
  623. if ($this->debug) {
  624. $prefix = ($isOutput) ? 'Out' : 'In';
  625. $ztype = (empty($type)) ? 'false' : $type;
  626. ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
  627. }
  628. /*
  629. See http://phplens.com/lens/lensforum/msgs.php?id=7231
  630. RETVAL is HARD CODED into php_mssql extension:
  631. The return value (a long integer value) is treated like a special OUTPUT parameter,
  632. called "RETVAL" (without the @). See the example at mssql_execute to
  633. see how it works. - type: one of this new supported PHP constants.
  634. SQLTEXT, SQLVARCHAR,SQLCHAR, SQLINT1,SQLINT2, SQLINT4, SQLBIT,SQLFLT8
  635. */
  636. if ($name !== 'RETVAL') $name = '@'.$name;
  637. return mssql_bind($stmt[1], $name, $var, $type, $isOutput, $isNull, $maxLen);
  638. }
  639. /*
  640. Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
  641. So all your blobs must be of type "image".
  642. Remember to set in php.ini the following...
  643. ; Valid range 0 - 2147483647. Default = 4096.
  644. mssql.textlimit = 0 ; zero to pass through
  645. ; Valid range 0 - 2147483647. Default = 4096.
  646. mssql.textsize = 0 ; zero to pass through
  647. */
  648. function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
  649. {
  650. if (strtoupper($blobtype) == 'CLOB') {
  651. $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
  652. return $this->Execute($sql) != false;
  653. }
  654. $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
  655. return $this->Execute($sql) != false;
  656. }
  657. // returns query ID if successful, otherwise false
  658. function _query($sql,$inputarr=false)
  659. {
  660. $this->_errorMsg = false;
  661. if (is_array($inputarr)) {
  662. # bind input params with sp_executesql:
  663. # see http://www.quest-pipelines.com/newsletter-v3/0402_F.htm
  664. # works only with sql server 7 and newer
  665. $getIdentity = false;
  666. if (!is_array($sql) && preg_match('/^\\s*insert/i', $sql)) {
  667. $getIdentity = true;
  668. $sql .= (preg_match('/;\\s*$/i', $sql) ? ' ' : '; ') . $this->identitySQL;
  669. }
  670. if (!is_array($sql)) $sql = $this->Prepare($sql);
  671. $params = '';
  672. $decl = '';
  673. $i = 0;
  674. foreach($inputarr as $v) {
  675. if ($decl) {
  676. $decl .= ', ';
  677. $params .= ', ';
  678. }
  679. if (is_string($v)) {
  680. $len = strlen($v);
  681. if ($len == 0) $len = 1;
  682. if ($len > 4000 ) {
  683. // NVARCHAR is max 4000 chars. Let's use NTEXT
  684. $decl .= "@P$i NTEXT";
  685. } else {
  686. $decl .= "@P$i NVARCHAR($len)";
  687. }
  688. if (substr($v,0,1) == "'" && substr($v,-1,1) == "'")
  689. /*
  690. * String is already fully quoted
  691. */
  692. $inputVar = $v;
  693. else
  694. $inputVar = $this->qstr($v);
  695. $params .= "@P$i=N" . $inputVar;
  696. } else if (is_integer($v)) {
  697. $decl .= "@P$i INT";
  698. $params .= "@P$i=".$v;
  699. } else if (is_float($v)) {
  700. $decl .= "@P$i FLOAT";
  701. $params .= "@P$i=".$v;
  702. } else if (is_bool($v)) {
  703. $decl .= "@P$i INT"; # Used INT just in case BIT in not supported on the user's MSSQL version. It will cast appropriately.
  704. $params .= "@P$i=".(($v)?'1':'0'); # True == 1 in MSSQL BIT fields and acceptable for storing logical true in an int field
  705. } else {
  706. $decl .= "@P$i CHAR"; # Used char because a type is required even when the value is to be NULL.
  707. $params .= "@P$i=NULL";
  708. }
  709. $i += 1;
  710. }
  711. $decl = $this->qstr($decl);
  712. if ($this->debug) ADOConnection::outp("<font size=-1>sp_executesql N{$sql[1]},N$decl,$params</font>");
  713. $rez = mssql_query("sp_executesql N{$sql[1]},N$decl,$params", $this->_connectionID);
  714. if ($getIdentity) {
  715. $arr = @mssql_fetch_row($rez);
  716. $this->lastInsID = isset($arr[0]) ? $arr[0] : false;
  717. @mssql_data_seek($rez, 0);
  718. }
  719. } else if (is_array($sql)) {
  720. # PrepareSP()
  721. $rez = mssql_execute($sql[1]);
  722. $this->lastInsID = false;
  723. } else {
  724. $rez = mssql_query($sql,$this->_connectionID);
  725. $this->lastInsID = false;
  726. }
  727. return $rez;
  728. }
  729. // returns true or false
  730. function _close()
  731. {
  732. if ($this->transCnt) $this->RollbackTrans();
  733. $rez = @mssql_close($this->_connectionID);
  734. $this->_connectionID = false;
  735. return $rez;
  736. }
  737. // mssql uses a default date like Dec 30 2000 12:00AM
  738. static function UnixDate($v)
  739. {
  740. return ADORecordSet_array_mssql::UnixDate($v);
  741. }
  742. static function UnixTimeStamp($v)
  743. {
  744. return ADORecordSet_array_mssql::UnixTimeStamp($v);
  745. }
  746. }
  747. /*--------------------------------------------------------------------------------------
  748. Class Name: Recordset
  749. --------------------------------------------------------------------------------------*/
  750. class ADORecordset_mssql extends ADORecordSet {
  751. var $databaseType = "mssql";
  752. var $canSeek = true;
  753. var $hasFetchAssoc; // see http://phplens.com/lens/lensforum/msgs.php?id=6083
  754. // _mths works only in non-localised system
  755. function __construct($id,$mode=false)
  756. {
  757. // freedts check...
  758. $this->hasFetchAssoc = function_exists('mssql_fetch_assoc');
  759. if ($mode === false) {
  760. global $ADODB_FETCH_MODE;
  761. $mode = $ADODB_FETCH_MODE;
  762. }
  763. $this->fetchMode = $mode;
  764. return parent::__construct($id,$mode);
  765. }
  766. function _initrs()
  767. {
  768. GLOBAL $ADODB_COUNTRECS;
  769. $this->_numOfRows = ($ADODB_COUNTRECS)? @mssql_num_rows($this->_queryID):-1;
  770. $this->_numOfFields = @mssql_num_fields($this->_queryID);
  771. }
  772. //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
  773. // get next resultset - requires PHP 4.0.5 or later
  774. function NextRecordSet()
  775. {
  776. if (!mssql_next_result($this->_queryID)) return false;
  777. $this->_inited = false;
  778. $this->bind = false;
  779. $this->_currentRow = -1;
  780. $this->Init();
  781. return true;
  782. }
  783. /* Use associative array to get fields array */
  784. function Fields($colname)
  785. {
  786. if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
  787. if (!$this->bind) {
  788. $this->bind = array();
  789. for ($i=0; $i < $this->_numOfFields; $i++) {
  790. $o = $this->FetchField($i);
  791. $this->bind[strtoupper($o->name)] = $i;
  792. }
  793. }
  794. return $this->fields[$this->bind[strtoupper($colname)]];
  795. }
  796. /* Returns: an object containing field information.
  797. Get column information in the Recordset object. fetchField() can be used in order to obtain information about
  798. fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
  799. fetchField() is retrieved. */
  800. function FetchField($fieldOffset = -1)
  801. {
  802. if ($fieldOffset != -1) {
  803. $f = @mssql_fetch_field($this->_queryID, $fieldOffset);
  804. }
  805. else if ($fieldOffset == -1) { /* The $fieldOffset argument is not provided thus its -1 */
  806. $f = @mssql_fetch_field($this->_queryID);
  807. }
  808. $false = false;
  809. if (empty($f)) return $false;
  810. return $f;
  811. }
  812. function _seek($row)
  813. {
  814. return @mssql_data_seek($this->_queryID, $row);
  815. }
  816. // speedup
  817. function MoveNext()
  818. {
  819. if ($this->EOF) return false;
  820. $this->_currentRow++;
  821. if ($this->fetchMode & ADODB_FETCH_ASSOC) {
  822. if ($this->fetchMode & ADODB_FETCH_NUM) {
  823. //ADODB_FETCH_BOTH mode
  824. $this->fields = @mssql_fetch_array($this->_queryID);
  825. }
  826. else {
  827. if ($this->hasFetchAssoc) {// only for PHP 4.2.0 or later
  828. $this->fields = @mssql_fetch_assoc($this->_queryID);
  829. } else {
  830. $flds = @mssql_fetch_array($this->_queryID);
  831. if (is_array($flds)) {
  832. $fassoc = array();
  833. foreach($flds as $k => $v) {
  834. if (is_numeric($k)) continue;
  835. $fassoc[$k] = $v;
  836. }
  837. $this->fields = $fassoc;
  838. } else
  839. $this->fields = false;
  840. }
  841. }
  842. if (is_array($this->fields)) {
  843. if (ADODB_ASSOC_CASE == 0) {
  844. foreach($this->fields as $k=>$v) {
  845. $kn = strtolower($k);
  846. if ($kn <> $k) {
  847. unset($this->fields[$k]);
  848. $this->fields[$kn] = $v;
  849. }
  850. }
  851. } else if (ADODB_ASSOC_CASE == 1) {
  852. foreach($this->fields as $k=>$v) {
  853. $kn = strtoupper($k);
  854. if ($kn <> $k) {
  855. unset($this->fields[$k]);
  856. $this->fields[$kn] = $v;
  857. }
  858. }
  859. }
  860. }
  861. } else {
  862. $this->fields = @mssql_fetch_row($this->_queryID);
  863. }
  864. if ($this->fields) return true;
  865. $this->EOF = true;
  866. return false;
  867. }
  868. // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
  869. // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
  870. function _fetch($ignore_fields=false)
  871. {
  872. if ($this->fetchMode & ADODB_FETCH_ASSOC) {
  873. if ($this->fetchMode & ADODB_FETCH_NUM) {
  874. //ADODB_FETCH_BOTH mode
  875. $this->fields = @mssql_fetch_array($this->_queryID);
  876. } else {
  877. if ($this->hasFetchAssoc) // only for PHP 4.2.0 or later
  878. $this->fields = @mssql_fetch_assoc($this->_queryID);
  879. else {
  880. $this->fields = @mssql_fetch_array($this->_queryID);
  881. if (@is_array($$this->fields)) {
  882. $fassoc = array();
  883. foreach($$this->fields as $k => $v) {
  884. if (is_integer($k)) continue;
  885. $fassoc[$k] = $v;
  886. }
  887. $this->fields = $fassoc;
  888. }
  889. }
  890. }
  891. if (!$this->fields) {
  892. } else if (ADODB_ASSOC_CASE == 0) {
  893. foreach($this->fields as $k=>$v) {
  894. $kn = strtolower($k);
  895. if ($kn <> $k) {
  896. unset($this->fields[$k]);
  897. $this->fields[$kn] = $v;
  898. }
  899. }
  900. } else if (ADODB_ASSOC_CASE == 1) {
  901. foreach($this->fields as $k=>$v) {
  902. $kn = strtoupper($k);
  903. if ($kn <> $k) {
  904. unset($this->fields[$k]);
  905. $this->fields[$kn] = $v;
  906. }
  907. }
  908. }
  909. } else {
  910. $this->fields = @mssql_fetch_row($this->_queryID);
  911. }
  912. return $this->fields;
  913. }
  914. /* close() only needs to be called if you are worried about using too much memory while your script
  915. is running. All associated result memory for the specified result identifier will automatically be freed. */
  916. function _close()
  917. {
  918. if($this->_queryID) {
  919. $rez = mssql_free_result($this->_queryID);
  920. $this->_queryID = false;
  921. return $rez;
  922. }
  923. return true;
  924. }
  925. // mssql uses a default date like Dec 30 2000 12:00AM
  926. static function UnixDate($v)
  927. {
  928. return ADORecordSet_array_mssql::UnixDate($v);
  929. }
  930. static function UnixTimeStamp($v)
  931. {
  932. return ADORecordSet_array_mssql::UnixTimeStamp($v);
  933. }
  934. }
  935. class ADORecordSet_array_mssql extends ADORecordSet_array {
  936. function __construct($id=-1,$mode=false)
  937. {
  938. parent::__construct($id,$mode);
  939. }
  940. // mssql uses a default date like Dec 30 2000 12:00AM
  941. static function UnixDate($v)
  942. {
  943. if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v);
  944. global $ADODB_mssql_mths,$ADODB_mssql_date_order;
  945. //Dec 30 2000 12:00AM
  946. if ($ADODB_mssql_date_order == 'dmy') {
  947. if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
  948. return parent::UnixDate($v);
  949. }
  950. if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
  951. $theday = $rr[1];
  952. $themth = substr(strtoupper($rr[2]),0,3);
  953. } else {
  954. if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
  955. return parent::UnixDate($v);
  956. }
  957. if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
  958. $theday = $rr[2];
  959. $themth = substr(strtoupper($rr[1]),0,3);
  960. }
  961. $themth = $ADODB_mssql_mths[$themth];
  962. if ($themth <= 0) return false;
  963. // h-m-s-MM-DD-YY
  964. return mktime(0,0,0,$themth,$theday,$rr[3]);
  965. }
  966. static function UnixTimeStamp($v)
  967. {
  968. if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v);
  969. global $ADODB_mssql_mths,$ADODB_mssql_date_order;
  970. //Dec 30 2000 12:00AM
  971. if ($ADODB_mssql_date_order == 'dmy') {
  972. if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|"
  973. ,$v, $rr)) return parent::UnixTimeStamp($v);
  974. if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
  975. $theday = $rr[1];
  976. $themth = substr(strtoupper($rr[2]),0,3);
  977. } else {
  978. if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|"
  979. ,$v, $rr)) return parent::UnixTimeStamp($v);
  980. if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
  981. $theday = $rr[2];
  982. $themth = substr(strtoupper($rr[1]),0,3);
  983. }
  984. $themth = $ADODB_mssql_mths[$themth];
  985. if ($themth <= 0) return false;
  986. switch (strtoupper($rr[6])) {
  987. case 'P':
  988. if ($rr[4]<12) $rr[4] += 12;
  989. break;
  990. case 'A':
  991. if ($rr[4]==12) $rr[4] = 0;
  992. break;
  993. default:
  994. break;
  995. }
  996. // h-m-s-MM-DD-YY
  997. return mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
  998. }
  999. }
  1000. /*
  1001. Code Example 1:
  1002. select object_name(constid) as constraint_name,
  1003. object_name(fkeyid) as table_name,
  1004. col_name(fkeyid, fkey) as column_name,
  1005. object_name(rkeyid) as referenced_table_name,
  1006. col_name(rkeyid, rkey) as referenced_column_name
  1007. from sysforeignkeys
  1008. where object_name(fkeyid) = x
  1009. order by constraint_name, table_name, referenced_table_name, keyno
  1010. Code Example 2:
  1011. select constraint_name,
  1012. column_name,
  1013. ordinal_position
  1014. from information_schema.key_column_usage
  1015. where constraint_catalog = db_name()
  1016. and table_name = x
  1017. order by constraint_name, ordinal_position
  1018. http://www.databasejournal.com/scripts/article.php/1440551
  1019. */