]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/adodb/drivers/adodb-odbtp.inc.php
Reformat code
[SourceForge/phpwiki.git] / lib / WikiDB / adodb / drivers / adodb-odbtp.inc.php
1 <?php
2 /*
3 V4.22 15 Apr 2004  (c) 2000-2004 John Lim (jlim@natsoft.com.my). All rights reserved.
4   Released under both BSD license and Lesser GPL library license.
5   Whenever there is any discrepancy between the two licenses,
6   the BSD license will take precedence. See License.txt.
7   Set tabs to 4 for best viewing.
8   Latest version is available at http://php.weblogs.com/
9 */
10 // Code contributed by "stefan bogdan" <sbogdan#rsb.ro>
11
12 define("_ADODB_ODBTP_LAYER", 2);
13
14 class ADODB_odbtp extends ADOConnection
15 {
16     var $databaseType = "odbtp";
17     var $dataProvider = "odbtp";
18     var $fmtDate = "'Y-m-d'";
19     var $fmtTimeStamp = "'Y-m-d, h:i:sA'";
20     var $replaceQuote = "''"; // string to use to replace quotes
21     var $odbc_driver = 0;
22     var $hasAffectedRows = true;
23     var $hasInsertID = false;
24     var $hasGenID = true;
25     var $hasMoveFirst = true;
26
27     var $_genSeqSQL = "create table %s (seq_name char(30) not null unique , seq_value integer not null)";
28     var $_dropSeqSQL = "delete from adodb_seq where seq_name = '%s'";
29     var $_autocommit = true;
30     var $_bindInputArray = false;
31     var $_useUnicodeSQL = false;
32     var $_canPrepareSP = false;
33
34     function ADODB_odbtp()
35     {
36     }
37
38     function ServerInfo()
39     {
40         return array('description' => @odbtp_get_attr(ODB_ATTR_DBMSNAME, $this->_connectionID),
41             'version' => @odbtp_get_attr(ODB_ATTR_DBMSVER, $this->_connectionID));
42     }
43
44     function ErrorMsg()
45     {
46         if (empty($this->_connectionID)) return @odbtp_last_error();
47         return @odbtp_last_error($this->_connectionID);
48     }
49
50     function ErrorNo()
51     {
52         if (empty($this->_connectionID)) return @odbtp_last_error_state();
53         return @odbtp_last_error_state($this->_connectionID);
54     }
55
56     function _insertid()
57     {
58         // SCOPE_IDENTITY()
59         // Returns the last IDENTITY value inserted into an IDENTITY column in
60         // the same scope. A scope is a module -- a stored procedure, trigger,
61         // function, or batch. Thus, two statements are in the same scope if
62         // they are in the same stored procedure, function, or batch.
63         return $this->GetOne($this->identitySQL);
64     }
65
66     function _affectedrows()
67     {
68         if ($this->_queryID) {
69             return @odbtp_affected_rows($this->_queryID);
70         } else
71             return 0;
72     }
73
74     function CreateSequence($seqname = 'adodbseq', $start = 1)
75     {
76         //verify existence
77         $num = $this->GetOne("select seq_value from adodb_seq");
78         $seqtab = 'adodb_seq';
79         if ($this->odbc_driver == ODB_DRIVER_FOXPRO) {
80             $path = @odbtp_get_attr(ODB_ATTR_DATABASENAME, $this->_connectionID);
81             //if using vfp dbc file
82             if (!strcasecmp(strrchr($path, '.'), '.dbc'))
83                 $path = substr($path, 0, strrpos($path, '\/'));
84             $seqtab = $path . '/' . $seqtab;
85         }
86         if ($num == false) {
87             if (empty($this->_genSeqSQL)) return false;
88             $ok = $this->Execute(sprintf($this->_genSeqSQL, $seqtab));
89         }
90         $num = $this->GetOne("select seq_value from adodb_seq where seq_name='$seqname'");
91         if ($num) {
92             return false;
93         }
94         $start -= 1;
95         return $this->Execute("insert into adodb_seq values('$seqname',$start)");
96     }
97
98     function DropSequence($seqname)
99     {
100         if (empty($this->_dropSeqSQL)) return false;
101         return $this->Execute(sprintf($this->_dropSeqSQL, $seqname));
102     }
103
104     function GenID($seq = 'adodbseq', $start = 1)
105     {
106         $seqtab = 'adodb_seq';
107         if ($this->odbc_driver == ODB_DRIVER_FOXPRO) {
108             $path = @odbtp_get_attr(ODB_ATTR_DATABASENAME, $this->_connectionID);
109             //if using vfp dbc file
110             if (!strcasecmp(strrchr($path, '.'), '.dbc'))
111                 $path = substr($path, 0, strrpos($path, '\/'));
112             $seqtab = $path . '/' . $seqtab;
113         }
114         $MAXLOOPS = 100;
115         while (--$MAXLOOPS >= 0) {
116             $num = $this->GetOne("select seq_value from adodb_seq where seq_name='$seq'");
117             if ($num === false) {
118                 //verify if abodb_seq table exist
119                 $ok = $this->GetOne("select seq_value from adodb_seq ");
120                 if (!$ok) {
121                     //creating the sequence table adodb_seq
122                     $this->Execute(sprintf($this->_genSeqSQL, $seqtab));
123                 }
124                 $start -= 1;
125                 $num = '0';
126                 $ok = $this->Execute("insert into adodb_seq values('$seq',$start)");
127                 if (!$ok) return false;
128             }
129             $ok = $this->Execute("update adodb_seq set seq_value=seq_value+1 where seq_name='$seq'");
130             if ($ok) {
131                 $num += 1;
132                 $this->genID = $num;
133                 return $num;
134             }
135         }
136         if ($fn = $this->raiseErrorFn) {
137             $fn($this->databaseType, 'GENID', -32000, "Unable to generate unique id after $MAXLOOPS attempts", $seq, $num);
138         }
139         return false;
140     }
141
142     //example for $UserOrDSN
143     //for visual fox : DRIVER={Microsoft Visual FoxPro Driver};SOURCETYPE=DBF;SOURCEDB=c:\YourDbfFileDir;EXCLUSIVE=NO;
144     //for visual fox dbc: DRIVER={Microsoft Visual FoxPro Driver};SOURCETYPE=DBC;SOURCEDB=c:\YourDbcFileDir\mydb.dbc;EXCLUSIVE=NO;
145     //for access : DRIVER={Microsoft Access Driver (*.mdb)};DBQ=c:\path_to_access_db\base_test.mdb;UID=root;PWD=;
146     //for mssql : DRIVER={SQL Server};SERVER=myserver;UID=myuid;PWD=mypwd;DATABASE=OdbtpTest;
147     //if uid & pwd can be separate
148     function _connect($HostOrInterface, $UserOrDSN = '', $argPassword = '', $argDatabase = '')
149     {
150         $this->_connectionID = @odbtp_connect($HostOrInterface, $UserOrDSN, $argPassword, $argDatabase);
151         if ($this->_connectionID === false) {
152             $this->_errorMsg = $this->ErrorMsg();
153             return false;
154         }
155         $this->odbc_driver = @odbtp_get_attr(ODB_ATTR_DRIVER, $this->_connectionID);
156
157         // Set driver specific attributes
158         switch ($this->odbc_driver) {
159             case ODB_DRIVER_MSSQL:
160                 $this->fmtDate = "'Y-m-d'";
161                 $this->fmtTimeStamp = "'Y-m-d h:i:sA'";
162                 $this->sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
163                 $this->sysTimeStamp = 'GetDate()';
164                 $this->ansiOuter = true;
165                 $this->leftOuter = '*=';
166                 $this->rightOuter = '=*';
167                 $this->hasTop = 'top';
168                 $this->hasInsertID = true;
169                 $this->hasTransactions = true;
170                 $this->_bindInputArray = true;
171                 $this->_canSelectDb = true;
172                 $this->substr = "substring";
173                 $this->length = 'len';
174                 $this->upperCase = 'upper';
175                 $this->identitySQL = 'select @@IDENTITY';
176                 $this->metaDatabasesSQL = "select name from master..sysdatabases where name <> 'master'";
177                 break;
178             case ODB_DRIVER_JET:
179                 $this->fmtDate = "#Y-m-d#";
180                 $this->fmtTimeStamp = "#Y-m-d h:i:sA#";
181                 $this->sysDate = "FORMAT(NOW,'yyyy-mm-dd')";
182                 $this->sysTimeStamp = 'NOW';
183                 $this->hasTop = 'top';
184                 $this->hasTransactions = false;
185                 $this->_canPrepareSP = true; // For MS Access only.
186
187                 // Can't rebind ODB_CHAR to ODB_WCHAR if row cache enabled.
188                 if ($this->_useUnicodeSQL)
189                     odbtp_use_row_cache($this->_connectionID, FALSE, 0);
190                 break;
191             case ODB_DRIVER_FOXPRO:
192                 $this->fmtDate = "{^Y-m-d}";
193                 $this->fmtTimeStamp = "{^Y-m-d, h:i:sA}";
194                 $this->sysDate = 'date()';
195                 $this->sysTimeStamp = 'datetime()';
196                 $this->ansiOuter = true;
197                 $this->hasTop = 'top';
198                 $this->hasTransactions = false;
199                 $this->replaceQuote = "'+chr(39)+'";
200                 $this->true = '.T.';
201                 $this->false = '.F.';
202                 $this->upperCase = 'upper';
203                 break;
204             case ODB_DRIVER_ORACLE:
205                 $this->fmtDate = "'Y-m-d 00:00:00'";
206                 $this->fmtTimeStamp = "'Y-m-d h:i:sA'";
207                 $this->sysDate = 'TRUNC(SYSDATE)';
208                 $this->sysTimeStamp = 'SYSDATE';
209                 $this->hasTransactions = true;
210                 $this->_bindInputArray = true;
211                 $this->concat_operator = '||';
212                 break;
213             case ODB_DRIVER_SYBASE:
214                 $this->fmtDate = "'Y-m-d'";
215                 $this->fmtTimeStamp = "'Y-m-d H:i:s'";
216                 $this->sysDate = 'GetDate()';
217                 $this->sysTimeStamp = 'GetDate()';
218                 $this->leftOuter = '*=';
219                 $this->rightOuter = '=*';
220                 $this->hasInsertID = true;
221                 $this->hasTransactions = true;
222                 $this->upperCase = 'upper';
223                 $this->identitySQL = 'select @@IDENTITY';
224                 break;
225             default:
226                 if (@odbtp_get_attr(ODB_ATTR_TXNCAPABLE, $this->_connectionID))
227                     $this->hasTransactions = true;
228                 else
229                     $this->hasTransactions = false;
230         }
231         @odbtp_set_attr(ODB_ATTR_FULLCOLINFO, TRUE, $this->_connectionID);
232         if ($this->_useUnicodeSQL)
233             @odbtp_set_attr(ODB_ATTR_UNICODESQL, TRUE, $this->_connectionID);
234         return true;
235     }
236
237     function _pconnect($HostOrInterface, $UserOrDSN = '', $argPassword = '', $argDatabase = '')
238     {
239         return $this->_connect($HostOrInterface, $UserOrDSN, $argPassword, $argDatabase);
240     }
241
242     function SelectDB($dbName)
243     {
244         if (!@odbtp_select_db($dbName, $this->_connectionID)) {
245             return false;
246         }
247         $this->databaseName = $dbName;
248         return true;
249     }
250
251     function &MetaTables($ttype = '', $showSchema = false, $mask = false)
252     {
253         global $ADODB_FETCH_MODE;
254
255         $savem = $ADODB_FETCH_MODE;
256         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
257         $arr =& $this->GetArray("||SQLTables||||$ttype");
258         $ADODB_FETCH_MODE = $savem;
259
260         $arr2 = array();
261         for ($i = 0; $i < sizeof($arr); $i++) {
262             if ($arr[$i][3] == 'SYSTEM TABLE') continue;
263             if ($arr[$i][2])
264                 $arr2[] = $showSchema ? $arr[$i][1] . '.' . $arr[$i][2] : $arr[$i][2];
265         }
266         return $arr2;
267     }
268
269     function &MetaColumns($table, $upper = true)
270     {
271         global $ADODB_FETCH_MODE;
272
273         $schema = false;
274         $this->_findschema($table, $schema);
275         if ($upper) $table = strtoupper($table);
276
277         $savem = $ADODB_FETCH_MODE;
278         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
279         $rs = $this->Execute("||SQLColumns||$schema|$table");
280         $ADODB_FETCH_MODE = $savem;
281
282         if (!$rs) return false;
283
284         while (!$rs->EOF) {
285             //print_r($rs->fields);
286             if (strtoupper($rs->fields[2]) == $table) {
287                 $fld = new ADOFieldObject();
288                 $fld->name = $rs->fields[3];
289                 $fld->type = $rs->fields[5];
290                 $fld->max_length = $rs->fields[6];
291                 $fld->not_null = !empty($rs->fields[9]);
292                 $fld->scale = $rs->fields[7];
293                 if (!is_null($rs->fields[12])) {
294                     $fld->has_default = true;
295                     $fld->default_value = $rs->fields[12];
296                 }
297                 $retarr[strtoupper($fld->name)] = $fld;
298             } elseif (sizeof($retarr) > 0)
299                 break;
300             $rs->MoveNext();
301         }
302         $rs->Close();
303
304         return $retarr;
305     }
306
307     function &MetaPrimaryKeys($table, $owner = '')
308     {
309         global $ADODB_FETCH_MODE;
310
311         $savem = $ADODB_FETCH_MODE;
312         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
313         $arr =& $this->GetArray("||SQLPrimaryKeys||$owner|$table");
314         $ADODB_FETCH_MODE = $savem;
315
316         //print_r($arr);
317         $arr2 = array();
318         for ($i = 0; $i < sizeof($arr); $i++) {
319             if ($arr[$i][3]) $arr2[] = $arr[$i][3];
320         }
321         return $arr2;
322     }
323
324     function &MetaForeignKeys($table, $owner = '', $upper = false)
325     {
326         global $ADODB_FETCH_MODE;
327
328         $savem = $ADODB_FETCH_MODE;
329         $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
330         $constraints =& $this->GetArray("||SQLForeignKeys|||||$owner|$table");
331         $ADODB_FETCH_MODE = $savem;
332
333         $arr = false;
334         foreach ($constraints as $constr) {
335             //print_r($constr);
336             $arr[$constr[11]][$constr[2]][] = $constr[7] . '=' . $constr[3];
337         }
338         if (!$arr) return false;
339
340         $arr2 = array();
341
342         foreach ($arr as $k => $v) {
343             foreach ($v as $a => $b) {
344                 if ($upper) $a = strtoupper($a);
345                 $arr2[$a] = $b;
346             }
347         }
348         return $arr2;
349     }
350
351     function BeginTrans()
352     {
353         if (!$this->hasTransactions) return false;
354         if ($this->transOff) return true;
355         $this->transCnt += 1;
356         $this->_autocommit = false;
357         $rs = @odbtp_set_attr(ODB_ATTR_TRANSACTIONS, ODB_TXN_READUNCOMMITTED, $this->_connectionID);
358         if (!$rs) return false;
359         else return true;
360     }
361
362     function CommitTrans($ok = true)
363     {
364         if ($this->transOff) return true;
365         if (!$ok) return $this->RollbackTrans();
366         if ($this->transCnt) $this->transCnt -= 1;
367         $this->_autocommit = true;
368         if (($ret = odbtp_commit($this->_connectionID)))
369             $ret = @odbtp_set_attr(ODB_ATTR_TRANSACTIONS, ODB_TXN_NONE, $this->_connectionID);
370         //set transaction off
371         return $ret;
372     }
373
374     function RollbackTrans()
375     {
376         if ($this->transOff) return true;
377         if ($this->transCnt) $this->transCnt -= 1;
378         $this->_autocommit = true;
379         if (($ret = odbtp_rollback($this->_connectionID)))
380             $ret = @odbtp_set_attr(ODB_ATTR_TRANSACTIONS, ODB_TXN_NONE, $this->_connectionID);
381         //set transaction off
382         return $ret;
383     }
384
385     function &SelectLimit($sql, $nrows = -1, $offset = -1, $inputarr = false, $secs2cache = 0)
386     {
387         // TOP requires ORDER BY for Visual FoxPro
388         if ($this->odbc_driver == ODB_DRIVER_FOXPRO) {
389             if (!preg_match('/ORDER[ \t\r\n]+BY/is', $sql)) $sql .= ' ORDER BY 1';
390         }
391         return ADOConnection::SelectLimit($sql, $nrows, $offset, $inputarr, $secs2cache);
392     }
393
394     function Prepare($sql)
395     {
396         if (!$this->_bindInputArray) return $sql; // no binding
397         $stmt = odbtp_prepare($sql, $this->_connectionID);
398         if (!$stmt) {
399             //  print "Prepare Error for ($sql) ".$this->ErrorMsg()."<br>";
400             return $sql;
401         }
402         return array($sql, $stmt, false);
403     }
404
405     function PrepareSP($sql)
406     {
407         if (!$this->_canPrepareSP) return $sql; // Can't prepare procedures
408
409         $stmt = odbtp_prepare_proc($sql, $this->_connectionID);
410         if (!$stmt) return false;
411         return array($sql, $stmt);
412     }
413
414     /*
415     Usage:
416         $stmt = $db->PrepareSP('SP_RUNSOMETHING'); -- takes 2 params, @myid and @group
417
418         # note that the parameter does not have @ in front!
419         $db->Parameter($stmt,$id,'myid');
420         $db->Parameter($stmt,$group,'group',false,64);
421         $db->Parameter($stmt,$group,'photo',false,100000,ODB_BINARY);
422         $db->Execute($stmt);
423
424         @param $stmt Statement returned by Prepare() or PrepareSP().
425         @param $var PHP variable to bind to. Can set to null (for isNull support).
426         @param $name Name of stored procedure variable name to bind to.
427         @param [$isOutput] Indicates direction of parameter 0/false=IN  1=OUT  2= IN/OUT. This is ignored in odbtp.
428         @param [$maxLen] Holds an maximum length of the variable.
429         @param [$type] The data type of $var. Legal values depend on driver.
430
431         See odbtp_attach_param documentation at http://odbtp.sourceforge.net.
432     */
433     function Parameter(&$stmt, &$var, $name, $isOutput = false, $maxLen = 0, $type = 0)
434     {
435         if ($this->odbc_driver == ODB_DRIVER_JET) {
436             $name = '[' . $name . ']';
437             if (!$type && $this->_useUnicodeSQL
438                 && @odbtp_param_bindtype($stmt[1], $name) == ODB_CHAR
439             ) {
440                 $type = ODB_WCHAR;
441             }
442         } else {
443             $name = '@' . $name;
444         }
445         return odbtp_attach_param($stmt[1], $name, $var, $type, $maxLen);
446     }
447
448     /*
449         Insert a null into the blob field of the table first.
450         Then use UpdateBlob to store the blob.
451
452         Usage:
453
454         $conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)');
455         $conn->UpdateBlob('blobtable','blobcol',$blob,'id=1');
456     */
457
458     function UpdateBlob($table, $column, $val, $where, $blobtype = 'image')
459     {
460         $sql = "UPDATE $table SET $column = ? WHERE $where";
461         if (!($stmt = odbtp_prepare($sql, $this->_connectionID)))
462             return false;
463         if (!odbtp_input($stmt, 1, ODB_BINARY, 1000000, $blobtype))
464             return false;
465         if (!odbtp_set($stmt, 1, $val))
466             return false;
467         return odbtp_execute($stmt) != false;
468     }
469
470     function IfNull($field, $ifNull)
471     {
472         switch ($this->odbc_driver) {
473             case ODB_DRIVER_MSSQL:
474                 return " ISNULL($field, $ifNull) ";
475             case ODB_DRIVER_JET:
476                 return " IIF(IsNull($field), $ifNull, $field) ";
477         }
478         return " CASE WHEN $field is null THEN $ifNull ELSE $field END ";
479     }
480
481     function _query($sql, $inputarr = false)
482     {
483         if ($inputarr) {
484             if (is_array($sql)) {
485                 $stmtid = $sql[1];
486             } else {
487                 $stmtid = odbtp_prepare($sql, $this->_connectionID);
488                 if ($stmtid == false) {
489                     $this->_errorMsg = $php_errormsg;
490                     return false;
491                 }
492             }
493             $num_params = odbtp_num_params($stmtid);
494             for ($param = 1; $param <= $num_params; $param++) {
495                 @odbtp_input($stmtid, $param);
496                 @odbtp_set($stmtid, $param, $inputarr[$param - 1]);
497             }
498             if (!odbtp_execute($stmtid)) {
499                 return false;
500             }
501         } elseif (is_array($sql)) {
502             $stmtid = $sql[1];
503             if (!odbtp_execute($stmtid)) {
504                 return false;
505             }
506         } else {
507             $stmtid = @odbtp_query($sql, $this->_connectionID);
508         }
509         $this->_lastAffectedRows = 0;
510         if ($stmtid) {
511             $this->_lastAffectedRows = @odbtp_affected_rows($stmtid);
512         }
513         return $stmtid;
514     }
515
516     function _close()
517     {
518         $ret = @odbtp_close($this->_connectionID);
519         $this->_connectionID = false;
520         return $ret;
521     }
522 }
523
524 class ADORecordSet_odbtp extends ADORecordSet
525 {
526
527     var $databaseType = 'odbtp';
528     var $canSeek = true;
529
530     function ADORecordSet_odbtp($queryID, $mode = false)
531     {
532         if ($mode === false) {
533             global $ADODB_FETCH_MODE;
534             $mode = $ADODB_FETCH_MODE;
535         }
536         $this->fetchMode = $mode;
537         $this->ADORecordSet($queryID);
538     }
539
540     function _initrs()
541     {
542         $this->_numOfFields = @odbtp_num_fields($this->_queryID);
543         if (!($this->_numOfRows = @odbtp_num_rows($this->_queryID)))
544             $this->_numOfRows = -1;
545     }
546
547     function &FetchField($fieldOffset = 0)
548     {
549         $off = $fieldOffset; // offsets begin at 0
550         $o = new ADOFieldObject();
551         $o->name = @odbtp_field_name($this->_queryID, $off);
552         $o->type = @odbtp_field_type($this->_queryID, $off);
553         $o->max_length = @odbtp_field_length($this->_queryID, $off);
554         if (ADODB_ASSOC_CASE == 0) $o->name = strtolower($o->name);
555         else if (ADODB_ASSOC_CASE == 1) $o->name = strtoupper($o->name);
556         return $o;
557     }
558
559     function _seek($row)
560     {
561         return @odbtp_data_seek($this->_queryID, $row);
562     }
563
564     function fields($colname)
565     {
566         if ($this->fetchMode & ADODB_FETCH_ASSOC) return $this->fields[$colname];
567
568         if (!$this->bind) {
569             $this->bind = array();
570             for ($i = 0; $i < $this->_numOfFields; $i++) {
571                 $name = @odbtp_field_name($this->_queryID, $i);
572                 $this->bind[strtoupper($name)] = $i;
573             }
574         }
575         return $this->fields[$this->bind[strtoupper($colname)]];
576     }
577
578     function _fetch_odbtp($type = 0)
579     {
580         switch ($this->fetchMode) {
581             case ADODB_FETCH_NUM:
582                 $this->fields = @odbtp_fetch_row($this->_queryID, $type);
583                 break;
584             case ADODB_FETCH_ASSOC:
585                 $this->fields = @odbtp_fetch_assoc($this->_queryID, $type);
586                 break;
587             default:
588                 $this->fields = @odbtp_fetch_array($this->_queryID, $type);
589         }
590         return is_array($this->fields);
591     }
592
593     function _fetch()
594     {
595         return $this->_fetch_odbtp();
596     }
597
598     function MoveFirst()
599     {
600         if (!$this->_fetch_odbtp(ODB_FETCH_FIRST)) return false;
601         $this->EOF = false;
602         $this->_currentRow = 0;
603         return true;
604     }
605
606     function MoveLast()
607     {
608         if (!$this->_fetch_odbtp(ODB_FETCH_LAST)) return false;
609         $this->EOF = false;
610         $this->_currentRow = $this->_numOfRows - 1;
611         return true;
612     }
613
614     function NextRecordSet()
615     {
616         if (!@odbtp_next_result($this->_queryID)) return false;
617         $this->_inited = false;
618         $this->bind = false;
619         $this->_currentRow = -1;
620         $this->Init();
621         return true;
622     }
623
624     function _close()
625     {
626         return @odbtp_free_query($this->_queryID);
627     }
628 }