source: branches/comu-ver2/data/module/DB/mssql.php @ 17877

Revision 17877, 28.6 KB checked in by Seasoft, 15 years ago (diff)

・PEAR::DB を Ver.1.7.14RC1(2007-11-27)ベースに更改。
・不適切な文字列比較を修正。

 http://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=1808&forum=9

  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?php
2
3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5/**
6 * The PEAR DB driver for PHP's mssql extension
7 * for interacting with Microsoft SQL Server databases
8 *
9 * PHP versions 4 and 5
10 *
11 * LICENSE: This source file is subject to version 3.0 of the PHP license
12 * that is available through the world-wide-web at the following URI:
13 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
14 * the PHP License and are unable to obtain it through the web, please
15 * send a note to license@php.net so we can mail you a copy immediately.
16 *
17 * @category   Database
18 * @package    DB
19 * @author     Sterling Hughes <sterling@php.net>
20 * @author     Daniel Convissor <danielc@php.net>
21 * @copyright  1997-2007 The PHP Group
22 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
23 * @version    CVS: $Id$
24 * @link       http://pear.php.net/package/DB
25 */
26
27/**
28 * Obtain the DB_common class so it can be extended from
29 */
30require_once 'DB/common.php';
31
32/**
33 * The methods PEAR DB uses to interact with PHP's mssql extension
34 * for interacting with Microsoft SQL Server databases
35 *
36 * These methods overload the ones declared in DB_common.
37 *
38 * DB's mssql driver is only for Microsfoft SQL Server databases.
39 *
40 * If you're connecting to a Sybase database, you MUST specify "sybase"
41 * as the "phptype" in the DSN.
42 *
43 * This class only works correctly if you have compiled PHP using
44 * --with-mssql=[dir_to_FreeTDS].
45 *
46 * @category   Database
47 * @package    DB
48 * @author     Sterling Hughes <sterling@php.net>
49 * @author     Daniel Convissor <danielc@php.net>
50 * @copyright  1997-2007 The PHP Group
51 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
52 * @version    Release: 1.7.14RC1
53 * @link       http://pear.php.net/package/DB
54 */
55class DB_mssql extends DB_common
56{
57    // {{{ properties
58
59    /**
60     * The DB driver type (mysql, oci8, odbc, etc.)
61     * @var string
62     */
63    var $phptype = 'mssql';
64
65    /**
66     * The database syntax variant to be used (db2, access, etc.), if any
67     * @var string
68     */
69    var $dbsyntax = 'mssql';
70
71    /**
72     * The capabilities of this DB implementation
73     *
74     * The 'new_link' element contains the PHP version that first provided
75     * new_link support for this DBMS.  Contains false if it's unsupported.
76     *
77     * Meaning of the 'limit' element:
78     *   + 'emulate' = emulate with fetch row by number
79     *   + 'alter'   = alter the query
80     *   + false     = skip rows
81     *
82     * @var array
83     */
84    var $features = array(
85        'limit'         => 'emulate',
86        'new_link'      => false,
87        'numrows'       => true,
88        'pconnect'      => true,
89        'prepare'       => false,
90        'ssl'           => false,
91        'transactions'  => true,
92    );
93
94    /**
95     * A mapping of native error codes to DB error codes
96     * @var array
97     */
98    // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX
99    var $errorcode_map = array(
100        102   => DB_ERROR_SYNTAX,
101        110   => DB_ERROR_VALUE_COUNT_ON_ROW,
102        155   => DB_ERROR_NOSUCHFIELD,
103        156   => DB_ERROR_SYNTAX,
104        170   => DB_ERROR_SYNTAX,
105        207   => DB_ERROR_NOSUCHFIELD,
106        208   => DB_ERROR_NOSUCHTABLE,
107        245   => DB_ERROR_INVALID_NUMBER,
108        319   => DB_ERROR_SYNTAX,
109        321   => DB_ERROR_NOSUCHFIELD,
110        325   => DB_ERROR_SYNTAX,
111        336   => DB_ERROR_SYNTAX,
112        515   => DB_ERROR_CONSTRAINT_NOT_NULL,
113        547   => DB_ERROR_CONSTRAINT,
114        1018  => DB_ERROR_SYNTAX,
115        1035  => DB_ERROR_SYNTAX,
116        1913  => DB_ERROR_ALREADY_EXISTS,
117        2209  => DB_ERROR_SYNTAX,
118        2223  => DB_ERROR_SYNTAX,
119        2248  => DB_ERROR_SYNTAX,
120        2256  => DB_ERROR_SYNTAX,
121        2257  => DB_ERROR_SYNTAX,
122        2627  => DB_ERROR_CONSTRAINT,
123        2714  => DB_ERROR_ALREADY_EXISTS,
124        3607  => DB_ERROR_DIVZERO,
125        3701  => DB_ERROR_NOSUCHTABLE,
126        7630  => DB_ERROR_SYNTAX,
127        8134  => DB_ERROR_DIVZERO,
128        9303  => DB_ERROR_SYNTAX,
129        9317  => DB_ERROR_SYNTAX,
130        9318  => DB_ERROR_SYNTAX,
131        9331  => DB_ERROR_SYNTAX,
132        9332  => DB_ERROR_SYNTAX,
133        15253 => DB_ERROR_SYNTAX,
134    );
135
136    /**
137     * The raw database connection created by PHP
138     * @var resource
139     */
140    var $connection;
141
142    /**
143     * The DSN information for connecting to a database
144     * @var array
145     */
146    var $dsn = array();
147
148
149    /**
150     * Should data manipulation queries be committed automatically?
151     * @var bool
152     * @access private
153     */
154    var $autocommit = true;
155
156    /**
157     * The quantity of transactions begun
158     *
159     * {@internal  While this is private, it can't actually be designated
160     * private in PHP 5 because it is directly accessed in the test suite.}}
161     *
162     * @var integer
163     * @access private
164     */
165    var $transaction_opcount = 0;
166
167    /**
168     * The database specified in the DSN
169     *
170     * It's a fix to allow calls to different databases in the same script.
171     *
172     * @var string
173     * @access private
174     */
175    var $_db = null;
176
177
178    // }}}
179    // {{{ constructor
180
181    /**
182     * This constructor calls <kbd>$this->DB_common()</kbd>
183     *
184     * @return void
185     */
186    function DB_mssql()
187    {
188        $this->DB_common();
189    }
190
191    // }}}
192    // {{{ connect()
193
194    /**
195     * Connect to the database server, log in and open the database
196     *
197     * Don't call this method directly.  Use DB::connect() instead.
198     *
199     * @param array $dsn         the data source name
200     * @param bool  $persistent  should the connection be persistent?
201     *
202     * @return int  DB_OK on success. A DB_Error object on failure.
203     */
204    function connect($dsn, $persistent = false)
205    {
206        if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase')
207            && !PEAR::loadExtension('sybase_ct'))
208        {
209            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
210        }
211
212        $this->dsn = $dsn;
213        if ($dsn['dbsyntax']) {
214            $this->dbsyntax = $dsn['dbsyntax'];
215        }
216
217        $params = array(
218            $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
219            $dsn['username'] ? $dsn['username'] : null,
220            $dsn['password'] ? $dsn['password'] : null,
221        );
222        if ($dsn['port']) {
223            $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':')
224                        . $dsn['port'];
225        }
226
227        $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect';
228
229        $this->connection = @call_user_func_array($connect_function, $params);
230
231        if (!$this->connection) {
232            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
233                                     null, null, null,
234                                     @mssql_get_last_message());
235        }
236        if ($dsn['database']) {
237            if (!@mssql_select_db($dsn['database'], $this->connection)) {
238                return $this->raiseError(DB_ERROR_NODBSELECTED,
239                                         null, null, null,
240                                         @mssql_get_last_message());
241            }
242            $this->_db = $dsn['database'];
243        }
244        return DB_OK;
245    }
246
247    // }}}
248    // {{{ disconnect()
249
250    /**
251     * Disconnects from the database server
252     *
253     * @return bool  TRUE on success, FALSE on failure
254     */
255    function disconnect()
256    {
257        $ret = @mssql_close($this->connection);
258        $this->connection = null;
259        return $ret;
260    }
261
262    // }}}
263    // {{{ simpleQuery()
264
265    /**
266     * Sends a query to the database server
267     *
268     * @param string  the SQL query string
269     *
270     * @return mixed  + a PHP result resrouce for successful SELECT queries
271     *                + the DB_OK constant for other successful queries
272     *                + a DB_Error object on failure
273     */
274    function simpleQuery($query)
275    {
276        $ismanip = $this->_checkManip($query);
277        $this->last_query = $query;
278        if (!@mssql_select_db($this->_db, $this->connection)) {
279            return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
280        }
281        $query = $this->modifyQuery($query);
282        if (!$this->autocommit && $ismanip) {
283            if ($this->transaction_opcount == 0) {
284                $result = @mssql_query('BEGIN TRAN', $this->connection);
285                if (!$result) {
286                    return $this->mssqlRaiseError();
287                }
288            }
289            $this->transaction_opcount++;
290        }
291        $result = @mssql_query($query, $this->connection);
292        if (!$result) {
293            return $this->mssqlRaiseError();
294        }
295        // Determine which queries that should return data, and which
296        // should return an error code only.
297        return $ismanip ? DB_OK : $result;
298    }
299
300    // }}}
301    // {{{ nextResult()
302
303    /**
304     * Move the internal mssql result pointer to the next available result
305     *
306     * @param a valid fbsql result resource
307     *
308     * @access public
309     *
310     * @return true if a result is available otherwise return false
311     */
312    function nextResult($result)
313    {
314        return @mssql_next_result($result);
315    }
316
317    // }}}
318    // {{{ fetchInto()
319
320    /**
321     * Places a row from the result set into the given array
322     *
323     * Formating of the array and the data therein are configurable.
324     * See DB_result::fetchInto() for more information.
325     *
326     * This method is not meant to be called directly.  Use
327     * DB_result::fetchInto() instead.  It can't be declared "protected"
328     * because DB_result is a separate object.
329     *
330     * @param resource $result    the query result resource
331     * @param array    $arr       the referenced array to put the data in
332     * @param int      $fetchmode how the resulting array should be indexed
333     * @param int      $rownum    the row number to fetch (0 = first row)
334     *
335     * @return mixed  DB_OK on success, NULL when the end of a result set is
336     *                 reached or on failure
337     *
338     * @see DB_result::fetchInto()
339     */
340    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
341    {
342        if ($rownum !== null) {
343            if (!@mssql_data_seek($result, $rownum)) {
344                return null;
345            }
346        }
347        if ($fetchmode & DB_FETCHMODE_ASSOC) {
348            $arr = @mssql_fetch_assoc($result);
349            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
350                $arr = array_change_key_case($arr, CASE_LOWER);
351            }
352        } else {
353            $arr = @mssql_fetch_row($result);
354        }
355        if (!$arr) {
356            return null;
357        }
358        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
359            $this->_rtrimArrayValues($arr);
360        }
361        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
362            $this->_convertNullArrayValuesToEmpty($arr);
363        }
364        return DB_OK;
365    }
366
367    // }}}
368    // {{{ freeResult()
369
370    /**
371     * Deletes the result set and frees the memory occupied by the result set
372     *
373     * This method is not meant to be called directly.  Use
374     * DB_result::free() instead.  It can't be declared "protected"
375     * because DB_result is a separate object.
376     *
377     * @param resource $result  PHP's query result resource
378     *
379     * @return bool  TRUE on success, FALSE if $result is invalid
380     *
381     * @see DB_result::free()
382     */
383    function freeResult($result)
384    {
385        return is_resource($result) ? mssql_free_result($result) : false;
386    }
387
388    // }}}
389    // {{{ numCols()
390
391    /**
392     * Gets the number of columns in a result set
393     *
394     * This method is not meant to be called directly.  Use
395     * DB_result::numCols() instead.  It can't be declared "protected"
396     * because DB_result is a separate object.
397     *
398     * @param resource $result  PHP's query result resource
399     *
400     * @return int  the number of columns.  A DB_Error object on failure.
401     *
402     * @see DB_result::numCols()
403     */
404    function numCols($result)
405    {
406        $cols = @mssql_num_fields($result);
407        if (!$cols) {
408            return $this->mssqlRaiseError();
409        }
410        return $cols;
411    }
412
413    // }}}
414    // {{{ numRows()
415
416    /**
417     * Gets the number of rows in a result set
418     *
419     * This method is not meant to be called directly.  Use
420     * DB_result::numRows() instead.  It can't be declared "protected"
421     * because DB_result is a separate object.
422     *
423     * @param resource $result  PHP's query result resource
424     *
425     * @return int  the number of rows.  A DB_Error object on failure.
426     *
427     * @see DB_result::numRows()
428     */
429    function numRows($result)
430    {
431        $rows = @mssql_num_rows($result);
432        if ($rows === false) {
433            return $this->mssqlRaiseError();
434        }
435        return $rows;
436    }
437
438    // }}}
439    // {{{ autoCommit()
440
441    /**
442     * Enables or disables automatic commits
443     *
444     * @param bool $onoff  true turns it on, false turns it off
445     *
446     * @return int  DB_OK on success.  A DB_Error object if the driver
447     *               doesn't support auto-committing transactions.
448     */
449    function autoCommit($onoff = false)
450    {
451        // XXX if $this->transaction_opcount > 0, we should probably
452        // issue a warning here.
453        $this->autocommit = $onoff ? true : false;
454        return DB_OK;
455    }
456
457    // }}}
458    // {{{ commit()
459
460    /**
461     * Commits the current transaction
462     *
463     * @return int  DB_OK on success.  A DB_Error object on failure.
464     */
465    function commit()
466    {
467        if ($this->transaction_opcount > 0) {
468            if (!@mssql_select_db($this->_db, $this->connection)) {
469                return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
470            }
471            $result = @mssql_query('COMMIT TRAN', $this->connection);
472            $this->transaction_opcount = 0;
473            if (!$result) {
474                return $this->mssqlRaiseError();
475            }
476        }
477        return DB_OK;
478    }
479
480    // }}}
481    // {{{ rollback()
482
483    /**
484     * Reverts the current transaction
485     *
486     * @return int  DB_OK on success.  A DB_Error object on failure.
487     */
488    function rollback()
489    {
490        if ($this->transaction_opcount > 0) {
491            if (!@mssql_select_db($this->_db, $this->connection)) {
492                return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
493            }
494            $result = @mssql_query('ROLLBACK TRAN', $this->connection);
495            $this->transaction_opcount = 0;
496            if (!$result) {
497                return $this->mssqlRaiseError();
498            }
499        }
500        return DB_OK;
501    }
502
503    // }}}
504    // {{{ affectedRows()
505
506    /**
507     * Determines the number of rows affected by a data maniuplation query
508     *
509     * 0 is returned for queries that don't manipulate data.
510     *
511     * @return int  the number of rows.  A DB_Error object on failure.
512     */
513    function affectedRows()
514    {
515        if ($this->_last_query_manip) {
516            $res = @mssql_query('select @@rowcount', $this->connection);
517            if (!$res) {
518                return $this->mssqlRaiseError();
519            }
520            $ar = @mssql_fetch_row($res);
521            if (!$ar) {
522                $result = 0;
523            } else {
524                @mssql_free_result($res);
525                $result = $ar[0];
526            }
527        } else {
528            $result = 0;
529        }
530        return $result;
531    }
532
533    // }}}
534    // {{{ nextId()
535
536    /**
537     * Returns the next free id in a sequence
538     *
539     * @param string  $seq_name  name of the sequence
540     * @param boolean $ondemand  when true, the seqence is automatically
541     *                            created if it does not exist
542     *
543     * @return int  the next id number in the sequence.
544     *               A DB_Error object on failure.
545     *
546     * @see DB_common::nextID(), DB_common::getSequenceName(),
547     *      DB_mssql::createSequence(), DB_mssql::dropSequence()
548     */
549    function nextId($seq_name, $ondemand = true)
550    {
551        $seqname = $this->getSequenceName($seq_name);
552        if (!@mssql_select_db($this->_db, $this->connection)) {
553            return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
554        }
555        $repeat = 0;
556        do {
557            $this->pushErrorHandling(PEAR_ERROR_RETURN);
558            $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
559            $this->popErrorHandling();
560            if ($ondemand && DB::isError($result) &&
561                ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
562            {
563                $repeat = 1;
564                $result = $this->createSequence($seq_name);
565                if (DB::isError($result)) {
566                    return $this->raiseError($result);
567                }
568            } elseif (!DB::isError($result)) {
569                $result = $this->query("SELECT IDENT_CURRENT('$seqname')");
570                if (DB::isError($result)) {
571                    /* Fallback code for MS SQL Server 7.0, which doesn't have
572                     * IDENT_CURRENT. This is *not* safe for concurrent
573                     * requests, and really, if you're using it, you're in a
574                     * world of hurt. Nevertheless, it's here to ensure BC. See
575                     * bug #181 for the gory details.*/
576                    $result = $this->query("SELECT @@IDENTITY FROM $seqname");
577                }
578                $repeat = 0;
579            } else {
580                $repeat = false;
581            }
582        } while ($repeat);
583        if (DB::isError($result)) {
584            return $this->raiseError($result);
585        }
586        $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
587        return $result[0];
588    }
589
590    /**
591     * Creates a new sequence
592     *
593     * @param string $seq_name  name of the new sequence
594     *
595     * @return int  DB_OK on success.  A DB_Error object on failure.
596     *
597     * @see DB_common::createSequence(), DB_common::getSequenceName(),
598     *      DB_mssql::nextID(), DB_mssql::dropSequence()
599     */
600    function createSequence($seq_name)
601    {
602        return $this->query('CREATE TABLE '
603                            . $this->getSequenceName($seq_name)
604                            . ' ([id] [int] IDENTITY (1, 1) NOT NULL,'
605                            . ' [vapor] [int] NULL)');
606    }
607
608    // }}}
609    // {{{ dropSequence()
610
611    /**
612     * Deletes a sequence
613     *
614     * @param string $seq_name  name of the sequence to be deleted
615     *
616     * @return int  DB_OK on success.  A DB_Error object on failure.
617     *
618     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
619     *      DB_mssql::nextID(), DB_mssql::createSequence()
620     */
621    function dropSequence($seq_name)
622    {
623        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
624    }
625
626    // }}}
627    // {{{ quoteIdentifier()
628
629    /**
630     * Quotes a string so it can be safely used as a table or column name
631     *
632     * @param string $str  identifier name to be quoted
633     *
634     * @return string  quoted identifier string
635     *
636     * @see DB_common::quoteIdentifier()
637     * @since Method available since Release 1.6.0
638     */
639    function quoteIdentifier($str)
640    {
641        return '[' . str_replace(']', ']]', $str) . ']';
642    }
643
644    // }}}
645    // {{{ mssqlRaiseError()
646
647    /**
648     * Produces a DB_Error object regarding the current problem
649     *
650     * @param int $errno  if the error is being manually raised pass a
651     *                     DB_ERROR* constant here.  If this isn't passed
652     *                     the error information gathered from the DBMS.
653     *
654     * @return object  the DB_Error object
655     *
656     * @see DB_common::raiseError(),
657     *      DB_mssql::errorNative(), DB_mssql::errorCode()
658     */
659    function mssqlRaiseError($code = null)
660    {
661        $message = @mssql_get_last_message();
662        if (!$code) {
663            $code = $this->errorNative();
664        }
665        return $this->raiseError($this->errorCode($code, $message),
666                                 null, null, null, "$code - $message");
667    }
668
669    // }}}
670    // {{{ errorNative()
671
672    /**
673     * Gets the DBMS' native error code produced by the last query
674     *
675     * @return int  the DBMS' error code
676     */
677    function errorNative()
678    {
679        $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection);
680        if (!$res) {
681            return DB_ERROR;
682        }
683        $row = @mssql_fetch_row($res);
684        return $row[0];
685    }
686
687    // }}}
688    // {{{ errorCode()
689
690    /**
691     * Determines PEAR::DB error code from mssql's native codes.
692     *
693     * If <var>$nativecode</var> isn't known yet, it will be looked up.
694     *
695     * @param  mixed  $nativecode  mssql error code, if known
696     * @return integer  an error number from a DB error constant
697     * @see errorNative()
698     */
699    function errorCode($nativecode = null, $msg = '')
700    {
701        if (!$nativecode) {
702            $nativecode = $this->errorNative();
703        }
704        if (isset($this->errorcode_map[$nativecode])) {
705            if ($nativecode == 3701
706                && preg_match('/Cannot drop the index/i', $msg))
707            {
708                return DB_ERROR_NOT_FOUND;
709            }
710            return $this->errorcode_map[$nativecode];
711        } else {
712            return DB_ERROR;
713        }
714    }
715
716    // }}}
717    // {{{ tableInfo()
718
719    /**
720     * Returns information about a table or a result set
721     *
722     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
723     * is a table name.
724     *
725     * @param object|string  $result  DB_result object from a query or a
726     *                                 string containing the name of a table.
727     *                                 While this also accepts a query result
728     *                                 resource identifier, this behavior is
729     *                                 deprecated.
730     * @param int            $mode    a valid tableInfo mode
731     *
732     * @return array  an associative array with the information requested.
733     *                 A DB_Error object on failure.
734     *
735     * @see DB_common::tableInfo()
736     */
737    function tableInfo($result, $mode = null)
738    {
739        if (is_string($result)) {
740            /*
741             * Probably received a table name.
742             * Create a result resource identifier.
743             */
744            if (!@mssql_select_db($this->_db, $this->connection)) {
745                return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED);
746            }
747            $id = @mssql_query("SELECT * FROM $result WHERE 1=0",
748                               $this->connection);
749            $got_string = true;
750        } elseif (isset($result->result)) {
751            /*
752             * Probably received a result object.
753             * Extract the result resource identifier.
754             */
755            $id = $result->result;
756            $got_string = false;
757        } else {
758            /*
759             * Probably received a result resource identifier.
760             * Copy it.
761             * Deprecated.  Here for compatibility only.
762             */
763            $id = $result;
764            $got_string = false;
765        }
766
767        if (!is_resource($id)) {
768            return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA);
769        }
770
771        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
772            $case_func = 'strtolower';
773        } else {
774            $case_func = 'strval';
775        }
776
777        $count = @mssql_num_fields($id);
778        $res   = array();
779
780        if ($mode) {
781            $res['num_fields'] = $count;
782        }
783
784        for ($i = 0; $i < $count; $i++) {
785            if ($got_string) {
786                $flags = $this->_mssql_field_flags($result,
787                        @mssql_field_name($id, $i));
788                if (DB::isError($flags)) {
789                    return $flags;
790                }
791            } else {
792                $flags = '';
793            }
794
795            $res[$i] = array(
796                'table' => $got_string ? $case_func($result) : '',
797                'name'  => $case_func(@mssql_field_name($id, $i)),
798                'type'  => @mssql_field_type($id, $i),
799                'len'   => @mssql_field_length($id, $i),
800                'flags' => $flags,
801            );
802            if ($mode & DB_TABLEINFO_ORDER) {
803                $res['order'][$res[$i]['name']] = $i;
804            }
805            if ($mode & DB_TABLEINFO_ORDERTABLE) {
806                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
807            }
808        }
809
810        // free the result only if we were called on a table
811        if ($got_string) {
812            @mssql_free_result($id);
813        }
814        return $res;
815    }
816
817    // }}}
818    // {{{ _mssql_field_flags()
819
820    /**
821     * Get a column's flags
822     *
823     * Supports "not_null", "primary_key",
824     * "auto_increment" (mssql identity), "timestamp" (mssql timestamp),
825     * "unique_key" (mssql unique index, unique check or primary_key) and
826     * "multiple_key" (multikey index)
827     *
828     * mssql timestamp is NOT similar to the mysql timestamp so this is maybe
829     * not useful at all - is the behaviour of mysql_field_flags that primary
830     * keys are alway unique? is the interpretation of multiple_key correct?
831     *
832     * @param string $table   the table name
833     * @param string $column  the field name
834     *
835     * @return string  the flags
836     *
837     * @access private
838     * @author Joern Barthel <j_barthel@web.de>
839     */
840    function _mssql_field_flags($table, $column)
841    {
842        static $tableName = null;
843        static $flags = array();
844
845        if ($table != $tableName) {
846
847            $flags = array();
848            $tableName = $table;
849
850            // get unique and primary keys
851            $res = $this->getAll("EXEC SP_HELPINDEX $table", DB_FETCHMODE_ASSOC);
852            if (DB::isError($res)) {
853                return $res;
854            }
855
856            foreach ($res as $val) {
857                $keys = explode(', ', $val['index_keys']);
858
859                if (sizeof($keys) > 1) {
860                    foreach ($keys as $key) {
861                        $this->_add_flag($flags[$key], 'multiple_key');
862                    }
863                }
864
865                if (strpos($val['index_description'], 'primary key')) {
866                    foreach ($keys as $key) {
867                        $this->_add_flag($flags[$key], 'primary_key');
868                    }
869                } elseif (strpos($val['index_description'], 'unique')) {
870                    foreach ($keys as $key) {
871                        $this->_add_flag($flags[$key], 'unique_key');
872                    }
873                }
874            }
875
876            // get auto_increment, not_null and timestamp
877            $res = $this->getAll("EXEC SP_COLUMNS $table", DB_FETCHMODE_ASSOC);
878            if (DB::isError($res)) {
879                return $res;
880            }
881
882            foreach ($res as $val) {
883                $val = array_change_key_case($val, CASE_LOWER);
884                if ($val['nullable'] == '0') {
885                    $this->_add_flag($flags[$val['column_name']], 'not_null');
886                }
887                if (strpos($val['type_name'], 'identity')) {
888                    $this->_add_flag($flags[$val['column_name']], 'auto_increment');
889                }
890                if (strpos($val['type_name'], 'timestamp')) {
891                    $this->_add_flag($flags[$val['column_name']], 'timestamp');
892                }
893            }
894        }
895
896        if (array_key_exists($column, $flags)) {
897            return(implode(' ', $flags[$column]));
898        }
899        return '';
900    }
901
902    // }}}
903    // {{{ _add_flag()
904
905    /**
906     * Adds a string to the flags array if the flag is not yet in there
907     * - if there is no flag present the array is created
908     *
909     * @param array  &$array  the reference to the flag-array
910     * @param string $value   the flag value
911     *
912     * @return void
913     *
914     * @access private
915     * @author Joern Barthel <j_barthel@web.de>
916     */
917    function _add_flag(&$array, $value)
918    {
919        if (!is_array($array)) {
920            $array = array($value);
921        } elseif (!in_array($value, $array)) {
922            array_push($array, $value);
923        }
924    }
925
926    // }}}
927    // {{{ getSpecialQuery()
928
929    /**
930     * Obtains the query string needed for listing a given type of objects
931     *
932     * @param string $type  the kind of objects you want to retrieve
933     *
934     * @return string  the SQL query string or null if the driver doesn't
935     *                  support the object type requested
936     *
937     * @access protected
938     * @see DB_common::getListOf()
939     */
940    function getSpecialQuery($type)
941    {
942        switch ($type) {
943            case 'tables':
944                return "SELECT name FROM sysobjects WHERE type = 'U'"
945                       . ' ORDER BY name';
946            case 'views':
947                return "SELECT name FROM sysobjects WHERE type = 'V'";
948            default:
949                return null;
950        }
951    }
952
953    // }}}
954}
955
956/*
957 * Local variables:
958 * tab-width: 4
959 * c-basic-offset: 4
960 * End:
961 */
962
963?>
Note: See TracBrowser for help on using the repository browser.