1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: 22: 23: 24: 25: 26: 27: 28:
29: class DboMssql extends DboSource {
30:
31: 32: 33: 34: 35:
36: var $description = "MS SQL DBO Driver";
37:
38: 39: 40: 41: 42:
43: var $startQuote = "[";
44:
45: 46: 47: 48: 49:
50: var $endQuote = "]";
51:
52: 53: 54: 55: 56: 57:
58: var $__fieldMappings = array();
59:
60: 61: 62: 63: 64:
65: var $_baseConfig = array(
66: 'persistent' => true,
67: 'host' => 'localhost',
68: 'login' => 'root',
69: 'password' => '',
70: 'database' => 'cake',
71: 'port' => '1433',
72: );
73:
74: 75: 76: 77: 78:
79: var $columns = array(
80: 'primary_key' => array('name' => 'IDENTITY (1, 1) NOT NULL'),
81: 'string' => array('name' => 'varchar', 'limit' => '255'),
82: 'text' => array('name' => 'text'),
83: 'integer' => array('name' => 'int', 'formatter' => 'intval'),
84: 'float' => array('name' => 'numeric', 'formatter' => 'floatval'),
85: 'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
86: 'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
87: 'time' => array('name' => 'datetime', 'format' => 'H:i:s', 'formatter' => 'date'),
88: 'date' => array('name' => 'datetime', 'format' => 'Y-m-d', 'formatter' => 'date'),
89: 'binary' => array('name' => 'image'),
90: 'boolean' => array('name' => 'bit')
91: );
92:
93: 94: 95: 96: 97: 98:
99: var $_commands = array(
100: 'begin' => 'BEGIN TRANSACTION',
101: 'commit' => 'COMMIT',
102: 'rollback' => 'ROLLBACK'
103: );
104:
105: 106: 107: 108: 109: 110:
111: var $__lastQueryHadError = false;
112: 113: 114: 115: 116: 117:
118: function __construct($config, $autoConnect = true) {
119: if ($autoConnect) {
120: if (!function_exists('mssql_min_message_severity')) {
121: trigger_error(__("PHP SQL Server interface is not installed, cannot continue. For troubleshooting information, see http://php.net/mssql/", true), E_USER_WARNING);
122: }
123: mssql_min_message_severity(15);
124: mssql_min_error_severity(2);
125: }
126: return parent::__construct($config, $autoConnect);
127: }
128:
129: 130: 131: 132: 133:
134: function connect() {
135: $config = $this->config;
136:
137: $os = env('OS');
138: if (!empty($os) && strpos($os, 'Windows') !== false) {
139: $sep = ',';
140: } else {
141: $sep = ':';
142: }
143: $this->connected = false;
144:
145: if (is_numeric($config['port'])) {
146: $port = $sep . $config['port'];
147: } elseif ($config['port'] === null) {
148: $port = '';
149: } else {
150: $port = '\\' . $config['port'];
151: }
152:
153: if (!$config['persistent']) {
154: $this->connection = mssql_connect($config['host'] . $port, $config['login'], $config['password'], true);
155: } else {
156: $this->connection = mssql_pconnect($config['host'] . $port, $config['login'], $config['password']);
157: }
158:
159: if (mssql_select_db($config['database'], $this->connection)) {
160: $this->_execute("SET DATEFORMAT ymd");
161: $this->connected = true;
162: }
163: return $this->connected;
164: }
165:
166: 167: 168: 169: 170:
171: function enabled() {
172: return extension_loaded('mssql');
173: }
174: 175: 176: 177: 178:
179: function disconnect() {
180: @mssql_free_result($this->results);
181: $this->connected = !@mssql_close($this->connection);
182: return !$this->connected;
183: }
184:
185: 186: 187: 188: 189: 190: 191:
192: function _execute($sql) {
193: $result = @mssql_query($sql, $this->connection);
194: $this->__lastQueryHadError = ($result === false);
195: return $result;
196: }
197:
198: 199: 200: 201: 202:
203: function listSources() {
204: $cache = parent::listSources();
205:
206: if ($cache != null) {
207: return $cache;
208: }
209: $result = $this->fetchAll('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES', false);
210:
211: if (!$result || empty($result)) {
212: return array();
213: } else {
214: $tables = array();
215:
216: foreach ($result as $table) {
217: $tables[] = $table[0]['TABLE_NAME'];
218: }
219:
220: parent::listSources($tables);
221: return $tables;
222: }
223: }
224:
225: 226: 227: 228: 229: 230:
231: function describe(&$model) {
232: $cache = parent::describe($model);
233:
234: if ($cache != null) {
235: return $cache;
236: }
237:
238: $table = $this->fullTableName($model, false);
239: $cols = $this->fetchAll("SELECT COLUMN_NAME as Field, DATA_TYPE as Type, COL_LENGTH('" . $table . "', COLUMN_NAME) as Length, IS_NULLABLE As [Null], COLUMN_DEFAULT as [Default], COLUMNPROPERTY(OBJECT_ID('" . $table . "'), COLUMN_NAME, 'IsIdentity') as [Key], NUMERIC_SCALE as Size FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" . $table . "'", false);
240:
241: $fields = false;
242: foreach ($cols as $column) {
243: $field = $column[0]['Field'];
244: $fields[$field] = array(
245: 'type' => $this->column($column[0]['Type']),
246: 'null' => (strtoupper($column[0]['Null']) == 'YES'),
247: 'default' => preg_replace("/^[(]{1,2}'?([^')]*)?'?[)]{1,2}$/", "$1", $column[0]['Default']),
248: 'length' => intval($column[0]['Length']),
249: 'key' => ($column[0]['Key'] == '1') ? 'primary' : false
250: );
251: if ($fields[$field]['default'] === 'null') {
252: $fields[$field]['default'] = null;
253: } else {
254: $this->value($fields[$field]['default'], $fields[$field]['type']);
255: }
256:
257: if ($fields[$field]['key'] && $fields[$field]['type'] == 'integer') {
258: $fields[$field]['length'] = 11;
259: } elseif (!$fields[$field]['key']) {
260: unset($fields[$field]['key']);
261: }
262: if (in_array($fields[$field]['type'], array('date', 'time', 'datetime', 'timestamp'))) {
263: $fields[$field]['length'] = null;
264: }
265: }
266: $this->__cacheDescription($this->fullTableName($model, false), $fields);
267: return $fields;
268: }
269:
270: 271: 272: 273: 274: 275: 276: 277:
278: function value($data, $column = null, $safe = false) {
279: $parent = parent::value($data, $column, $safe);
280:
281: if ($parent != null) {
282: return $parent;
283: }
284: if ($data === null) {
285: return 'NULL';
286: }
287: if (in_array($column, array('integer', 'float', 'binary')) && $data === '') {
288: return 'NULL';
289: }
290: if ($data === '') {
291: return "''";
292: }
293:
294: switch ($column) {
295: case 'boolean':
296: $data = $this->boolean((bool)$data);
297: break;
298: default:
299: if (get_magic_quotes_gpc()) {
300: $data = stripslashes(str_replace("'", "''", $data));
301: } else {
302: $data = str_replace("'", "''", $data);
303: }
304: break;
305: }
306:
307: if (in_array($column, array('integer', 'float', 'binary')) && is_numeric($data)) {
308: return $data;
309: }
310: return "'" . $data . "'";
311: }
312:
313: 314: 315: 316: 317: 318: 319: 320:
321: function fields(&$model, $alias = null, $fields = array(), $quote = true) {
322: if (empty($alias)) {
323: $alias = $model->alias;
324: }
325: $fields = parent::fields($model, $alias, $fields, false);
326: $count = count($fields);
327:
328: if (
329: $count >= 1 &&
330: strpos($fields[0], 'COUNT(*)') === false &&
331: strpos($fields[0], 'COUNT(DISTINCT') === false
332: ) {
333: $result = array();
334: for ($i = 0; $i < $count; $i++) {
335: $prepend = '';
336:
337: if (strpos($fields[$i], 'DISTINCT') !== false) {
338: $prepend = 'DISTINCT ';
339: $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
340: }
341: $fieldAlias = count($this->__fieldMappings);
342:
343: if (!preg_match('/\s+AS\s+/i', $fields[$i])) {
344: if (substr($fields[$i], -1) == '*') {
345: if (strpos($fields[$i], '.') !== false && $fields[$i] != $alias . '.*') {
346: $build = explode('.', $fields[$i]);
347: $AssociatedModel = $model->{$build[0]};
348: } else {
349: $AssociatedModel = $model;
350: }
351:
352: $_fields = $this->fields($AssociatedModel, $AssociatedModel->alias, array_keys($AssociatedModel->schema()));
353: $result = array_merge($result, $_fields);
354: continue;
355: }
356:
357: if (strpos($fields[$i], '.') === false) {
358: $this->__fieldMappings[$alias . '__' . $fieldAlias] = $alias . '.' . $fields[$i];
359: $fieldName = $this->name($alias . '.' . $fields[$i]);
360: $fieldAlias = $this->name($alias . '__' . $fieldAlias);
361: } else {
362: $build = explode('.', $fields[$i]);
363: $this->__fieldMappings[$build[0] . '__' . $fieldAlias] = $fields[$i];
364: $fieldName = $this->name($build[0] . '.' . $build[1]);
365: $fieldAlias = $this->name(preg_replace("/^\[(.+)\]$/", "$1", $build[0]) . '__' . $fieldAlias);
366: }
367: if ($model->getColumnType($fields[$i]) == 'datetime') {
368: $fieldName = "CONVERT(VARCHAR(20), {$fieldName}, 20)";
369: }
370: $fields[$i] = "{$fieldName} AS {$fieldAlias}";
371: }
372: $result[] = $prepend . $fields[$i];
373: }
374: return $result;
375: } else {
376: return $fields;
377: }
378: }
379:
380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390:
391: function create(&$model, $fields = null, $values = null) {
392: if (!empty($values)) {
393: $fields = array_combine($fields, $values);
394: }
395: $primaryKey = $this->_getPrimaryKey($model);
396:
397: if (array_key_exists($primaryKey, $fields)) {
398: if (empty($fields[$primaryKey])) {
399: unset($fields[$primaryKey]);
400: } else {
401: $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($model) . ' ON');
402: }
403: }
404: $result = parent::create($model, array_keys($fields), array_values($fields));
405: if (array_key_exists($primaryKey, $fields) && !empty($fields[$primaryKey])) {
406: $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($model) . ' OFF');
407: }
408: return $result;
409: }
410:
411: 412: 413: 414: 415: 416: 417: 418: 419: 420:
421: function update(&$model, $fields = array(), $values = null, $conditions = null) {
422: if (!empty($values)) {
423: $fields = array_combine($fields, $values);
424: }
425: if (isset($fields[$model->primaryKey])) {
426: unset($fields[$model->primaryKey]);
427: }
428: if (empty($fields)) {
429: return true;
430: }
431: return parent::update($model, array_keys($fields), array_values($fields), $conditions);
432: }
433:
434: 435: 436: 437: 438:
439: function lastError() {
440: if ($this->__lastQueryHadError) {
441: $error = mssql_get_last_message();
442: if ($error && !preg_match('/contexto de la base de datos a|contesto di database|changed database|contexte de la base de don|datenbankkontext/i', $error)) {
443: return $error;
444: }
445: }
446: return null;
447: }
448:
449: 450: 451: 452: 453: 454:
455: function lastAffected() {
456: if ($this->_result) {
457: return mssql_rows_affected($this->connection);
458: }
459: return null;
460: }
461:
462: 463: 464: 465: 466: 467:
468: function lastNumRows() {
469: if ($this->_result) {
470: return @mssql_num_rows($this->_result);
471: }
472: return null;
473: }
474:
475: 476: 477: 478: 479: 480:
481: function lastInsertId($source = null) {
482: $id = $this->fetchRow('SELECT SCOPE_IDENTITY() AS insertID', false);
483: return $id[0]['insertID'];
484: }
485:
486: 487: 488: 489: 490: 491: 492:
493: function limit($limit, $offset = null) {
494: if ($limit) {
495: $rt = '';
496: if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) {
497: $rt = ' TOP';
498: }
499: $rt .= ' ' . $limit;
500: if (is_int($offset) && $offset > 0) {
501: $rt .= ' OFFSET ' . $offset;
502: }
503: return $rt;
504: }
505: return null;
506: }
507:
508: 509: 510: 511: 512: 513:
514: function column($real) {
515: if (is_array($real)) {
516: $col = $real['name'];
517:
518: if (isset($real['limit'])) {
519: $col .= '(' . $real['limit'] . ')';
520: }
521: return $col;
522: }
523: $col = str_replace(')', '', $real);
524: $limit = null;
525: if (strpos($col, '(') !== false) {
526: list($col, $limit) = explode('(', $col);
527: }
528:
529: if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
530: return $col;
531: }
532: if ($col == 'bit') {
533: return 'boolean';
534: }
535: if (strpos($col, 'int') !== false) {
536: return 'integer';
537: }
538: if (strpos($col, 'char') !== false) {
539: return 'string';
540: }
541: if (strpos($col, 'text') !== false) {
542: return 'text';
543: }
544: if (strpos($col, 'binary') !== false || $col == 'image') {
545: return 'binary';
546: }
547: if (in_array($col, array('float', 'real', 'decimal', 'numeric'))) {
548: return 'float';
549: }
550: return 'text';
551: }
552:
553: 554: 555: 556: 557:
558: function resultSet(&$results) {
559: $this->results =& $results;
560: $this->map = array();
561: $numFields = mssql_num_fields($results);
562: $index = 0;
563: $j = 0;
564:
565: while ($j < $numFields) {
566: $column = mssql_field_name($results, $j);
567:
568: if (strpos($column, '__')) {
569: if (isset($this->__fieldMappings[$column]) && strpos($this->__fieldMappings[$column], '.')) {
570: $map = explode('.', $this->__fieldMappings[$column]);
571: } elseif (isset($this->__fieldMappings[$column])) {
572: $map = array(0, $this->__fieldMappings[$column]);
573: } else {
574: $map = array(0, $column);
575: }
576: $this->map[$index++] = $map;
577: } else {
578: $this->map[$index++] = array(0, $column);
579: }
580: $j++;
581: }
582: }
583:
584: 585: 586: 587: 588: 589: 590:
591: function renderStatement($type, $data) {
592: switch (strtolower($type)) {
593: case 'select':
594: extract($data);
595: $fields = trim($fields);
596:
597: if (strpos($limit, 'TOP') !== false && strpos($fields, 'DISTINCT ') === 0) {
598: $limit = 'DISTINCT ' . trim($limit);
599: $fields = substr($fields, 9);
600: }
601:
602: if (preg_match('/offset\s+([0-9]+)/i', $limit, $offset)) {
603: $limit = preg_replace('/\s*offset.*$/i', '', $limit);
604: preg_match('/top\s+([0-9]+)/i', $limit, $limitVal);
605: $offset = intval($offset[1]) + intval($limitVal[1]);
606: $rOrder = $this->__switchSort($order);
607: list($order2, $rOrder) = array($this->__mapFields($order), $this->__mapFields($rOrder));
608: return "SELECT * FROM (SELECT {$limit} * FROM (SELECT TOP {$offset} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}) AS Set1 {$rOrder}) AS Set2 {$order2}";
609: } else {
610: return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group} {$order}";
611: }
612: break;
613: case "schema":
614: extract($data);
615:
616: foreach ($indexes as $i => $index) {
617: if (preg_match('/PRIMARY KEY/', $index)) {
618: unset($indexes[$i]);
619: break;
620: }
621: }
622:
623: foreach (array('columns', 'indexes') as $var) {
624: if (is_array(${$var})) {
625: ${$var} = "\t" . implode(",\n\t", array_filter(${$var}));
626: }
627: }
628: return "CREATE TABLE {$table} (\n{$columns});\n{$indexes}";
629: break;
630: default:
631: return parent::renderStatement($type, $data);
632: break;
633: }
634: }
635:
636: 637: 638: 639: 640: 641: 642:
643: function __switchSort($order) {
644: $order = preg_replace('/\s+ASC/i', '__tmp_asc__', $order);
645: $order = preg_replace('/\s+DESC/i', ' ASC', $order);
646: return preg_replace('/__tmp_asc__/', ' DESC', $order);
647: }
648:
649: 650: 651: 652: 653: 654: 655:
656: function __mapFields($sql) {
657: if (empty($sql) || empty($this->__fieldMappings)) {
658: return $sql;
659: }
660: foreach ($this->__fieldMappings as $key => $val) {
661: $sql = preg_replace('/' . preg_quote($val) . '/', $this->name($key), $sql);
662: $sql = preg_replace('/' . preg_quote($this->name($val)) . '/', $this->name($key), $sql);
663: }
664: return $sql;
665: }
666:
667: 668: 669: 670: 671: 672: 673: 674:
675: function read(&$model, $queryData = array(), $recursive = null) {
676: $results = parent::read($model, $queryData, $recursive);
677: $this->__fieldMappings = array();
678: return $results;
679: }
680:
681: 682: 683: 684: 685:
686: function fetchResult() {
687: if ($row = mssql_fetch_row($this->results)) {
688: $resultRow = array();
689: $i = 0;
690:
691: foreach ($row as $index => $field) {
692: list($table, $column) = $this->map[$index];
693: $resultRow[$table][$column] = $row[$index];
694: $i++;
695: }
696: return $resultRow;
697: } else {
698: return false;
699: }
700: }
701:
702: 703: 704: 705: 706: 707: 708: 709:
710: function insertMulti($table, $fields, $values) {
711: $primaryKey = $this->_getPrimaryKey($table);
712: $hasPrimaryKey = $primaryKey != null && (
713: (is_array($fields) && in_array($primaryKey, $fields)
714: || (is_string($fields) && strpos($fields, $this->startQuote . $primaryKey . $this->endQuote) !== false))
715: );
716:
717: if ($hasPrimaryKey) {
718: $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON');
719: }
720: parent::insertMulti($table, $fields, $values);
721: if ($hasPrimaryKey) {
722: $this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' OFF');
723: }
724: }
725:
726: 727: 728: 729: 730: 731: 732:
733: function buildColumn($column) {
734: $result = preg_replace('/(int|integer)\([0-9]+\)/i', '$1', parent::buildColumn($column));
735: if (strpos($result, 'DEFAULT NULL') !== false) {
736: $result = str_replace('DEFAULT NULL', 'NULL', $result);
737: } else if (array_keys($column) == array('type', 'name')) {
738: $result .= ' NULL';
739: }
740: return $result;
741: }
742:
743: 744: 745: 746: 747: 748: 749:
750: function buildIndex($indexes, $table = null) {
751: $join = array();
752:
753: foreach ($indexes as $name => $value) {
754: if ($name == 'PRIMARY') {
755: $join[] = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
756: } else if (isset($value['unique']) && $value['unique']) {
757: $out = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE";
758:
759: if (is_array($value['column'])) {
760: $value['column'] = implode(', ', array_map(array(&$this, 'name'), $value['column']));
761: } else {
762: $value['column'] = $this->name($value['column']);
763: }
764: $out .= "({$value['column']});";
765: $join[] = $out;
766: }
767: }
768: return $join;
769: }
770:
771: 772: 773: 774: 775: 776: 777:
778: function _getPrimaryKey($model) {
779: if (is_object($model)) {
780: $schema = $model->schema();
781: } else {
782: $schema = $this->describe($model);
783: }
784:
785: foreach ($schema as $field => $props) {
786: if (isset($props['key']) && $props['key'] == 'primary') {
787: return $field;
788: }
789: }
790: return null;
791: }
792: }
793: