PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/sgl/includes/qcubed/_core/database/QSqlServerDatabase.class.php

http://logisticsouth.googlecode.com/
PHP | 775 lines | 564 code | 105 blank | 106 comment | 77 complexity | 260d6a2c4d2dd0be5214a463a5d02000 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * Database Adapter for Microsoft SQL Server
  4. * Utilizes the Microsoft SQL Server extension php_mssql.dll (win) or the freetds extension (*nix)
  5. *
  6. * NOTES:
  7. *
  8. * LimitInfo and Query utilizes an interal SQL tag QCODO_OFFSET<#>, where # represents
  9. * the number of rows to offset for "Limit"-based queries. The QCODO_OFFSET is added
  10. * internally by SqlLimitVariablePrefix(), and it is handled (and removed from the query)
  11. * by Query(). In error messages and DB profiling, the QCODO_OFFSET<#> tag *WILL* appear
  12. * (if applicable). The framework will handle this gracefully, but obviously, if you try
  13. * and cut and paste SQL code that contains QCODO_OFFSET<#> into QueryAnalyzer, the query
  14. * will fail, so just be aware of that. If you want to do something like test queries
  15. * with QueryAnalyzer, just remember to manually remove any QCODO_OFFSET<#> information.
  16. *
  17. * MSSQL will return DATE values according to regional settings. QCubed expects
  18. * dates to be of the format YYYY-MM-DD. Therefore, make sure that your
  19. * PHP.INI file says:
  20. * mssql.datetimeconvert = Off
  21. *
  22. * Also, if you are using MSSQL Server 2K from Linux/Unix (using FreeTDS), you may
  23. * encounter an issue with the following error:
  24. * WARNING! ! Some character(s) could not be converted into client's character set...
  25. * If so, then update your connection settings in the freetds.conf to say the following:
  26. * client charset = UTF-8
  27. * For consistancy, you will also want to update QApplication::$EncodingType to be UTF-8
  28. * as well.
  29. * @package DatabaseAdapters
  30. */
  31. class QSqlServerDatabase extends QDatabaseBase {
  32. const Adapter = 'Microsoft SQL Server Database Adapter';
  33. protected $objMsSql;
  34. protected $strEscapeIdentifierBegin = '[';
  35. protected $strEscapeIdentifierEnd = ']';
  36. /**
  37. * Properly escapes $mixData to be used as a SQL query parameter.
  38. * If IncludeEquality is set (usually not), then include an equality operator.
  39. * So for most data, it would just be "=". But, for example,
  40. * if $mixData is NULL, then most RDBMS's require the use of "IS".
  41. *
  42. * @param mixed $mixData
  43. * @param boolean $blnIncludeEquality whether or not to include an equality operator
  44. * @param boolean $blnReverseEquality whether the included equality operator should be a "NOT EQUAL", e.g. "!="
  45. * @return string the properly formatted SQL variable
  46. */
  47. public function SqlVariable($mixData, $blnIncludeEquality = false, $blnReverseEquality = false) {
  48. // Are we SqlVariabling a BOOLEAN value?
  49. if (is_bool($mixData)) {
  50. // Yes
  51. if ($blnIncludeEquality) {
  52. // We must include the inequality
  53. if ($blnReverseEquality) {
  54. // Do a "Reverse Equality"
  55. // Check against NULL, True then False
  56. if (is_null($mixData))
  57. return 'IS NOT NULL';
  58. else if ($mixData)
  59. return '= 0';
  60. else
  61. return '!= 0';
  62. } else {
  63. // Check against NULL, True then False
  64. if (is_null($mixData))
  65. return 'IS NULL';
  66. else if ($mixData)
  67. return '!= 0';
  68. else
  69. return '= 0';
  70. }
  71. } else {
  72. // Check against NULL, True then False
  73. if (is_null($mixData))
  74. return 'NULL';
  75. else if ($mixData)
  76. return '1';
  77. else
  78. return '0';
  79. }
  80. }
  81. // Check for Equality Inclusion
  82. if ($blnIncludeEquality) {
  83. if ($blnReverseEquality) {
  84. if (is_null($mixData))
  85. $strToReturn = 'IS NOT ';
  86. else
  87. $strToReturn = '!= ';
  88. } else {
  89. if (is_null($mixData))
  90. $strToReturn = 'IS ';
  91. else
  92. $strToReturn = '= ';
  93. }
  94. } else
  95. $strToReturn = '';
  96. // Check for NULL Value
  97. if (is_null($mixData))
  98. return $strToReturn . 'NULL';
  99. // Check for NUMERIC Value
  100. if (is_integer($mixData) || is_float($mixData))
  101. return $strToReturn . sprintf('%s', $mixData);
  102. // Check for DATE Value
  103. if ($mixData instanceof QDateTime)
  104. return $strToReturn . sprintf("'%s'", $mixData->qFormat(QDateTime::FormatIso));
  105. // Assume it's some kind of string value
  106. return $strToReturn . sprintf("'%s'", str_replace("'", "''", $mixData));
  107. }
  108. public function SqlLimitVariablePrefix($strLimitInfo) {
  109. // Setup limit suffix (if applicable) via a TOP clause
  110. // Add QCODO_OFFSET tag if applicable
  111. if (strlen($strLimitInfo)) {
  112. if (strpos($strLimitInfo, ';') !== false)
  113. throw new Exception('Invalid Semicolon in LIMIT Info');
  114. if (strpos($strLimitInfo, '`') !== false)
  115. throw new Exception('Invalid Backtick in LIMIT Info');
  116. // First figure out if we HAVE an offset
  117. $strArray = explode(',', $strLimitInfo);
  118. if (count($strArray) == 2) {
  119. // Yep -- there's an offset
  120. return sprintf(
  121. 'TOP %s QCODO_OFFSET<%s>',
  122. ($strArray[0] + $strArray[1]),
  123. $strArray[0]);
  124. } else if (count($strArray) == 1) {
  125. return 'TOP ' . $strArray[0];
  126. } else {
  127. throw new QSqlServerDatabaseException('Invalid Limit Info: ' . $strLimitInfo, 0, null);
  128. }
  129. }
  130. return null;
  131. }
  132. public function SqlLimitVariableSuffix($strLimitInfo) {
  133. return null;
  134. }
  135. public function SqlSortByVariable($strSortByInfo) {
  136. // Setup sorting information (if applicable) via a ORDER BY clause
  137. if (strlen($strSortByInfo)) {
  138. if (strpos($strSortByInfo, ';') !== false)
  139. throw new Exception('Invalid Semicolon in ORDER BY Info');
  140. if (strpos($strSortByInfo, '`') !== false)
  141. throw new Exception('Invalid Backtick in ORDER BY Info');
  142. return "ORDER BY $strSortByInfo";
  143. }
  144. return null;
  145. }
  146. public function Connect() {
  147. // Set several ini settings
  148. ini_set ( 'mssql.textlimit' , '65536' );
  149. ini_set ( 'mssql.textsize' , '65536' );
  150. ini_set ( 'mssql.charset' , 'utf-8' );
  151. ini_set ( 'mssql.datetimeconvert' , 'Off' );
  152. // Lookup Adapter-Specific Connection Properties
  153. $strServer = $this->Server;
  154. $strName = $this->Database;
  155. $strUsername = $this->Username;
  156. $strPassword = $this->Password;
  157. $strPort = $this->Port;
  158. if ($strPort) {
  159. // Windows Servers
  160. if (array_key_exists('OS', $_SERVER) && stristr($_SERVER['OS'], 'Win') !== false)
  161. $strServer .= ',' . $strPort;
  162. // All Other Servers
  163. else
  164. $strServer .= ':' . $strPort;
  165. }
  166. // Connect to the Database Server
  167. // Because the MSSQL extension throws warnings, we want to avoid them
  168. set_error_handler('QcodoHandleError', 0);
  169. $this->objMsSql = mssql_connect($strServer, $strUsername, $strPassword, true);
  170. if (!$this->objMsSql) {
  171. $objException = new QSqlServerDatabaseException('Unable to connect to Database: ' . mssql_get_last_message(), -1, null);
  172. $objException->IncrementOffset();
  173. throw $objException;
  174. }
  175. if (!mssql_select_db($strName, $this->objMsSql)) {
  176. $objException = new QSqlServerDatabaseException('Unable to connect to Database: ' . mssql_get_last_message(), -1, null);
  177. $objException->IncrementOffset();
  178. throw $objException;
  179. }
  180. // Restore the error handler to the original
  181. restore_error_handler();
  182. // Update Connected Flag
  183. $this->blnConnectedFlag = true;
  184. }
  185. public function __get($strName) {
  186. switch ($strName) {
  187. case 'AffectedRows':
  188. return mssql_affected_rows($this->objMsSql);
  189. default:
  190. try {
  191. return parent::__get($strName);
  192. } catch (QCallerException $objExc) {
  193. $objExc->IncrementOffset();
  194. throw $objExc;
  195. }
  196. }
  197. }
  198. public function Query($strQuery) {
  199. // Connect if Applicable
  200. if (!$this->blnConnectedFlag) $this->Connect();
  201. // Log Query (for Profiling, if applicable)
  202. $this->LogQuery($strQuery);
  203. // First, check for QCODO_OFFSET<#> for LIMIT INFO Offseting
  204. if ( ($intPosition = strpos($strQuery, 'QCODO_OFFSET<')) !== false) {
  205. $intEndPosition = strpos($strQuery, '>', $intPosition);
  206. if ($intEndPosition === false)
  207. throw new QSqlServerDatabaseException('Invalid QCODO_OFFSET', 0, $strQuery);
  208. $intOffset = QType::Cast(substr($strQuery,
  209. $intPosition + 13 /* len of QCODO_OFFSET< */,
  210. $intEndPosition - $intPosition - 13), QType::Integer);
  211. $strQuery = substr($strQuery, 0, $intPosition) . substr($strQuery, $intEndPosition + 1);
  212. } else
  213. $intOffset = 0;
  214. // Perform the Query
  215. // Because the MSSQL extension throws warnings, we want to make our mssql_query
  216. // call around no error handler
  217. set_error_handler('QcodoHandleError', 0);
  218. // To Avoid Long String Truncation
  219. mssql_query('SET TEXTSIZE 65536', $this->objMsSql);
  220. $objResult = mssql_query($strQuery, $this->objMsSql);
  221. restore_error_handler();
  222. if (!$objResult)
  223. throw new QSqlServerDatabaseException(mssql_get_last_message(), 0, $strQuery);
  224. // Return the Result
  225. $objSqlServerDatabaseResult = new QSqlServerDatabaseResult($objResult, $this);
  226. // Perform Offsetting (if applicable)
  227. for ($intIndex = 0; $intIndex < $intOffset; $intIndex++) {
  228. $objRow = $objSqlServerDatabaseResult->FetchRow();
  229. if (!$objRow)
  230. return $objSqlServerDatabaseResult;
  231. }
  232. return $objSqlServerDatabaseResult;
  233. }
  234. public function NonQuery($strNonQuery) {
  235. // Connect if Applicable
  236. if (!$this->blnConnectedFlag) $this->Connect();
  237. // Log Query (for Profiling, if applicable)
  238. $this->LogQuery($strNonQuery);
  239. // Perform the Query
  240. // Because the MSSQL extension throws warnings, we want to make our mssql_query
  241. // call around no error handler
  242. set_error_handler('QcodoHandleError', 0);
  243. $objResult = mssql_query($strNonQuery, $this->objMsSql);
  244. restore_error_handler();
  245. if (!$objResult)
  246. throw new QSqlServerDatabaseException(mssql_get_last_message(), 0, $strNonQuery);
  247. }
  248. public function GetTables() {
  249. // $objResult = $this->Query("SELECT name FROM sysobjects WHERE xtype='U' ORDER BY name ASC");
  250. $objResult = $this->Query("SELECT name FROM sysobjects WHERE (OBJECTPROPERTY(id, N'IsTable') = 1) AND " .
  251. "(name NOT LIKE N'#%') AND (OBJECTPROPERTY(id, N'IsMSShipped') = 0) AND (OBJECTPROPERTY(id, N'IsSystemTable') = 0) " .
  252. "ORDER BY name ASC");
  253. $strToReturn = array();
  254. while ($strRowArray = $objResult->FetchRow())
  255. array_push($strToReturn, $strRowArray[0]);
  256. return $strToReturn;
  257. }
  258. public function GetTableForId($intTableId) {
  259. $intTableId = $this->SqlVariable($intTableId);
  260. $strQuery = sprintf('
  261. SELECT
  262. name
  263. FROM
  264. sysobjects
  265. WHERE
  266. id = %s
  267. ', $intTableId);
  268. $objResult = $this->Query($strQuery);
  269. $objRow = $objResult->FetchRow();
  270. return $objRow[0];
  271. }
  272. public function GetFieldsForTable($strTableName) {
  273. $strTableName = $this->SqlVariable($strTableName);
  274. $strQuery = sprintf('
  275. SELECT
  276. syscolumns.*
  277. FROM
  278. syscolumns,
  279. sysobjects
  280. WHERE
  281. sysobjects.name = %s AND
  282. sysobjects.id = syscolumns.id
  283. ORDER BY
  284. colorder ASC
  285. ', $strTableName);
  286. $objResult = $this->Query($strQuery);
  287. $objFields = array();
  288. while ($objRow = $objResult->GetNextRow()) {
  289. array_push($objFields, new QSqlServerDatabaseField($objRow, $this));
  290. }
  291. return $objFields;
  292. }
  293. public function InsertId($strTableName = null, $strColumnName = null) {
  294. $strQuery = 'SELECT SCOPE_IDENTITY();';
  295. $objResult = $this->Query($strQuery);
  296. $objRow = $objResult->FetchRow();
  297. return $objRow[0];
  298. }
  299. public function Close() {
  300. mssql_close($this->objMsSql);
  301. }
  302. public function TransactionBegin() {
  303. $this->NonQuery('BEGIN TRANSACTION;');
  304. }
  305. public function TransactionCommit() {
  306. $this->NonQuery('COMMIT;');
  307. }
  308. public function TransactionRollback() {
  309. $this->NonQuery('ROLLBACK;');
  310. }
  311. public function GetIndexesForTable($strTableName) {
  312. $objIndexArray = array();
  313. // Use sp_helpindex to pull the indexes
  314. $objResult = $this->Query(sprintf('exec sp_helpindex %s', $this->SqlVariable($strTableName)));
  315. while ($objRow = $objResult->GetNextRow()) {
  316. $strIndexDescription = $objRow->GetColumn('index_description');
  317. $strKeyName = $objRow->GetColumn('index_name');
  318. $blnPrimaryKey = (strpos($strIndexDescription, 'primary key') !== false);
  319. $blnUnique = (strpos($strIndexDescription, 'unique') !== false);
  320. $strColumnNameArray = explode(', ', $objRow->GetColumn('index_keys'));
  321. $objIndex = new QDatabaseIndex($strKeyName, $blnPrimaryKey, $blnUnique, $strColumnNameArray);
  322. array_push($objIndexArray, $objIndex);
  323. }
  324. return $objIndexArray;
  325. }
  326. public function GetForeignKeysForTable($strTableName) {
  327. $objForeignKeyArray = array();
  328. // Use Query to pull the FKs
  329. $strQuery = sprintf('
  330. SELECT
  331. fk_table = FK.TABLE_NAME,
  332. fk_column = CU.COLUMN_NAME,
  333. pk_table = PK.TABLE_NAME,
  334. pk_column = PT.COLUMN_NAME,
  335. constraint_name = C.CONSTRAINT_NAME
  336. FROM
  337. INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C
  338. INNER JOIN
  339. INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK
  340. ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME
  341. INNER JOIN
  342. INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK
  343. ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME
  344. INNER JOIN
  345. INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU
  346. ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME
  347. INNER JOIN
  348. (
  349. SELECT
  350. i1.TABLE_NAME, i2.COLUMN_NAME
  351. FROM
  352. INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1
  353. INNER JOIN
  354. INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2
  355. ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME
  356. WHERE i1.CONSTRAINT_TYPE = \'PRIMARY KEY\'
  357. ) PT
  358. ON PT.TABLE_NAME = PK.TABLE_NAME
  359. WHERE
  360. FK.TABLE_NAME = %s
  361. ORDER BY
  362. constraint_name',
  363. $this->SqlVariable($strTableName));
  364. $objResult = $this->Query($strQuery);
  365. $strKeyName = '';
  366. while ($objRow = $objResult->GetNextRow()) {
  367. if ($strKeyName != $objRow->GetColumn('constraint_name')) {
  368. if ($strKeyName) {
  369. $objForeignKey = new QDatabaseForeignKey(
  370. $strKeyName,
  371. $strColumnNameArray,
  372. $strReferenceTableName,
  373. $strReferenceColumnNameArray);
  374. array_push($objForeignKeyArray, $objForeignKey);
  375. }
  376. $strKeyName = $objRow->GetColumn('constraint_name');
  377. $strReferenceTableName = $objRow->GetColumn('pk_table');
  378. $strColumnNameArray = array();
  379. $strReferenceColumnNameArray = array();
  380. }
  381. if (!array_search($objRow->GetColumn('fk_column'), $strColumnNameArray)) {
  382. array_push($strColumnNameArray, $objRow->GetColumn('fk_column'));
  383. }
  384. if (!array_search($objRow->GetColumn('pk_column'), $strReferenceColumnNameArray)) {
  385. array_push($strReferenceColumnNameArray, $objRow->GetColumn('pk_column'));
  386. }
  387. }
  388. if ($strKeyName) {
  389. $objForeignKey = new QDatabaseForeignKey(
  390. $strKeyName,
  391. $strColumnNameArray,
  392. $strReferenceTableName,
  393. $strReferenceColumnNameArray);
  394. array_push($objForeignKeyArray, $objForeignKey);
  395. }
  396. // Return the Array of Foreign Keys
  397. return $objForeignKeyArray;
  398. }
  399. }
  400. /**
  401. *
  402. * @package DatabaseAdapters
  403. */
  404. class QSqlServerDatabaseException extends QDatabaseExceptionBase {
  405. public function __construct($strMessage, $intNumber, $strQuery) {
  406. parent::__construct(sprintf("MS SQL Server Error: %s", $strMessage), 2);
  407. $this->intErrorNumber = $intNumber;
  408. $this->strQuery = $strQuery;
  409. }
  410. }
  411. /**
  412. *
  413. * @package DatabaseAdapters
  414. */
  415. class QSqlServerDatabaseResult extends QDatabaseResultBase {
  416. protected $objMsSqlResult;
  417. protected $objDb;
  418. public function __construct($objResult, QSqlServerDatabase $objDb) {
  419. $this->objMsSqlResult = $objResult;
  420. $this->objDb = $objDb;
  421. }
  422. public function FetchArray() {
  423. return mssql_fetch_array($this->objMsSqlResult);
  424. }
  425. public function FetchFields() {
  426. $objArrayToReturn = array();
  427. while ($objSqlServerDatabaseField = $this->FetchField())
  428. array_push($objArrayToReturn, $objSqlServerDatabaseField);
  429. return $objArrayToReturn;
  430. }
  431. public function FetchField() {
  432. if ($objField = mssql_fetch_field($this->objMsSqlResult))
  433. return new QSqlServerDatabaseField($objField, $this->objDb);
  434. }
  435. public function FetchRow() {
  436. return mssql_fetch_row($this->objMsSqlResult);
  437. }
  438. public function CountRows() {
  439. return mssql_num_rows($this->objMsSqlResult);
  440. }
  441. public function CountFields() {
  442. return mssql_num_fields($this->objMsSqlResult);
  443. }
  444. public function Close() {
  445. mssql_free_result($this->objMsSqlResult);
  446. }
  447. public function GetNextRow() {
  448. $strColumnArray = $this->FetchArray();
  449. if ($strColumnArray)
  450. return new QSqlServerDatabaseRow($strColumnArray);
  451. else
  452. return null;
  453. }
  454. public function GetRows() {
  455. $objDbRowArray = array();
  456. while ($objDbRow = $this->GetNextRow())
  457. array_push($objDbRowArray, $objDbRow);
  458. return $objDbRowArray;
  459. }
  460. }
  461. /**
  462. *
  463. * @package DatabaseAdapters
  464. */
  465. class QSqlServerDatabaseRow extends QDatabaseRowBase {
  466. protected $strColumnArray;
  467. public function __construct($strColumnArray) {
  468. $this->strColumnArray = $strColumnArray;
  469. }
  470. public function GetColumn($strColumnName, $strColumnType = null) {
  471. if (array_key_exists($strColumnName, $this->strColumnArray)) {
  472. if (is_null($this->strColumnArray[$strColumnName]))
  473. return null;
  474. switch ($strColumnType) {
  475. case QDatabaseFieldType::Bit:
  476. return ($this->strColumnArray[$strColumnName]) ? true : false;
  477. case QDatabaseFieldType::Blob:
  478. case QDatabaseFieldType::Char:
  479. case QDatabaseFieldType::VarChar:
  480. return QType::Cast($this->strColumnArray[$strColumnName], QType::String);
  481. case QDatabaseFieldType::Date:
  482. case QDatabaseFieldType::DateTime:
  483. case QDatabaseFieldType::Time:
  484. return new QDateTime($this->strColumnArray[$strColumnName]);
  485. case QDatabaseFieldType::Float:
  486. return QType::Cast($this->strColumnArray[$strColumnName], QType::Float);
  487. case QDatabaseFieldType::Integer:
  488. return QType::Cast($this->strColumnArray[$strColumnName], QType::Integer);
  489. default:
  490. return $this->strColumnArray[$strColumnName];
  491. }
  492. } else
  493. return null;
  494. }
  495. public function ColumnExists($strColumnName) {
  496. return array_key_exists($strColumnName, $this->strColumnArray);
  497. }
  498. public function GetColumnNameArray() {
  499. return $this->strColumnArray;
  500. }
  501. }
  502. /**
  503. *
  504. * @package DatabaseAdapters
  505. */
  506. class QSqlServerDatabaseField extends QDatabaseFieldBase {
  507. public function __construct($mixFieldData, $objDb = null) {
  508. $objDatabaseRow = null;
  509. try {
  510. $objDatabaseRow = QType::Cast($mixFieldData, 'QSqlServerDatabaseRow');
  511. } catch (QInvalidCastException $objExc) {
  512. }
  513. if ($objDatabaseRow) {
  514. // Passed in field data is a row from select * from syscolumns for this table
  515. $intTableId = $objDatabaseRow->GetColumn('id');
  516. $this->strName = $objDatabaseRow->GetColumn('name');
  517. $this->strOriginalName = $this->strName;
  518. $this->strTable = $objDb->GetTableForId($intTableId);
  519. $this->strOriginalTable = $this->strTable;
  520. $this->strDefault = null; /* Not Supported */
  521. $this->intMaxLength = $objDatabaseRow->GetColumn('length', QDatabaseFieldType::Integer);
  522. $this->blnNotNull = ($objDatabaseRow->GetColumn('isnullable')) ? false : true;
  523. // Determine Primary Key
  524. $objResult = $objDb->Query(sprintf("EXEC sp_pkeys @table_name='%s'", $this->strTable));
  525. while ($objRow = $objResult->GetNextRow()) {
  526. if ($objRow->GetColumn('COLUMN_NAME') == $this->strName)
  527. $this->blnPrimaryKey = true;
  528. }
  529. if (!$this->blnPrimaryKey)
  530. $this->blnPrimaryKey = false;
  531. // UNIQUE
  532. // First, we assume we're NOT unique
  533. $this->blnUnique = false;
  534. // Now, get all the single-column indexes for this table (by indid)
  535. $strQuery = sprintf('
  536. SELECT
  537. indid,
  538. count(indid) AS column_count
  539. FROM
  540. sysindexkeys
  541. WHERE
  542. id = %s
  543. GROUP BY
  544. indid', $intTableId);
  545. $objResult = $objDb->Query($strQuery);
  546. $intIndIdArray = array();
  547. while ($objRow = $objResult->GetNextRow())
  548. if ($objRow->GetColumn('column_count') == 1) {
  549. // We have a single-column index -- add it to the indid array
  550. array_push($intIndIdArray, $objRow->GetColumn('indid', QDatabasefieldtype::Integer));
  551. }
  552. if (count($intIndIdArray) > 0) {
  553. // Get all the single-column index that indexes this column
  554. $strQuery = sprintf('
  555. SELECT
  556. sysindexes.name
  557. FROM
  558. sysindexes,
  559. sysindexkeys,
  560. syscolumns
  561. WHERE
  562. syscolumns.colid = sysindexkeys.colid AND
  563. sysindexes.indid = sysindexkeys.indid AND
  564. sysindexkeys.indid IN (%s) AND
  565. syscolumns.name = %s AND
  566. syscolumns.id = %s AND
  567. sysindexkeys.id = %s AND
  568. sysindexes.id = %s
  569. ',
  570. implode(',', $intIndIdArray),
  571. $objDb->SqlVariable($this->strName),
  572. $intTableId,
  573. $intTableId,
  574. $intTableId);
  575. $objResult = $objDb->Query($strQuery);
  576. while ($objRow = $objResult->FetchRow()) {
  577. $strQuery = sprintf("SELECT indexproperty(%s, %s, 'IsUnique')",
  578. $intTableId, $objDb->SqlVariable($objRow[0]));
  579. $objIndexPropertyResult = $objDb->Query($strQuery);
  580. $objRow = $objIndexPropertyResult->FetchRow();
  581. if ($objRow[0])
  582. $this->blnUnique = true;
  583. }
  584. }
  585. // Figure out Type and Identity by using sp_columns
  586. $objResult = $objDb->Query(sprintf("EXEC sp_columns @table_name='%s', @column_name='%s'", $this->strTable, $this->strName));
  587. $objRow = $objResult->GetNextRow();
  588. $strTypeName = $objRow->GetColumn('TYPE_NAME');
  589. $intScale = $objRow->GetColumn('SCALE');
  590. $this->blnIdentity = (strpos($strTypeName, 'identity') !== false) ? true : false;
  591. // We're only going to use the first word of the TYPE_NAME
  592. if (strpos($strTypeName, ' ') !== false)
  593. $strTypeName = substr($strTypeName, 0, strpos($strTypeName, ' '));
  594. $this->strType = $strTypeName;
  595. switch ($strTypeName) {
  596. case 'numeric':
  597. case 'numeric()':
  598. case 'decimal':
  599. case 'decimal()':
  600. if ($intScale == 0)
  601. $this->strType = QDatabaseFieldType::Integer;
  602. else
  603. $this->strType = QDatabaseFieldType::Float;
  604. break;
  605. case 'bigint':
  606. case 'int':
  607. case 'tinyint':
  608. case 'smallint':
  609. $this->strType = QDatabaseFieldType::Integer;
  610. break;
  611. case 'money':
  612. case 'real':
  613. case 'float':
  614. case 'smallmoney':
  615. $this->strType = QDatabaseFieldType::Float;
  616. break;
  617. case 'bit':
  618. $this->strType = QDatabaseFieldType::Bit;
  619. break;
  620. case 'char':
  621. case 'nchar':
  622. $this->strType = QDatabaseFieldType::Char;
  623. break;
  624. case 'varchar':
  625. case 'nvarchar':
  626. $this->strType = QDatabaseFieldType::VarChar;
  627. break;
  628. case 'text':
  629. case 'ntext':
  630. case 'binary':
  631. case 'image':
  632. case 'varbinary':
  633. case 'uniqueidentifier':
  634. case 'unique_identifier':
  635. $this->strType = QDatabaseFieldType::Blob;
  636. $this->intMaxLength = null;
  637. break;
  638. case 'datetime':
  639. case 'smalldatetime':
  640. $this->strType = QDatabaseFieldType::DateTime;
  641. break;
  642. case 'date':
  643. $this->strType = QDatabaseFieldType::Date;
  644. break;
  645. case 'time':
  646. $this->strType = QDatabaseFieldType::Time;
  647. break;
  648. case 'timestamp':
  649. // System-generated Timestamp values need to be treated as plain text
  650. $this->strType = QDatabaseFieldType::VarChar;
  651. $this->blnTimestamp = true;
  652. break;
  653. default:
  654. throw new QSqlServerDatabaseException('Unsupported Field Type: ' . $strTypeName, 0, null);
  655. }
  656. } else {
  657. // Passed in fielddata is a mssql_fetch_field field result
  658. $this->strName = $mixFieldData->name;
  659. $this->strOriginalName = $mixFieldData->name;
  660. $this->strTable = $mixFieldData->column_source;
  661. $this->strOriginalTable = $mixFieldData->column_source;
  662. $this->intMaxLength = $mixFieldData->max_length;
  663. }
  664. }
  665. }
  666. ?>