Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
/**
3
 * SQLite3 driver
4
 *
5
 * @link https://www.sqlite.org/
6
 *
7
 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
8
 *
9
 * @package ADOdb
10
 * @link https://adodb.org Project's web site and documentation
11
 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
12
 *
13
 * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
14
 * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
15
 * any later version. This means you can use it in proprietary products.
16
 * See the LICENSE.md file distributed with this source code for details.
17
 * @license BSD-3-Clause
18
 * @license LGPL-2.1-or-later
19
 *
20
 * @copyright 2000-2013 John Lim
21
 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
22
 */
23
 
24
// security - hide paths
25
if (!defined('ADODB_DIR')) die();
26
 
27
/**
28
 * Class ADODB_sqlite3
29
 */
30
class ADODB_sqlite3 extends ADOConnection {
31
	var $databaseType = "sqlite3";
32
	var $dataProvider = "sqlite";
33
	var $replaceQuote = "''"; // string to use to replace quotes
34
	var $concat_operator='||';
35
	var $_errorNo = 0;
36
	var $hasLimit = true;
37
	var $hasInsertID = true; 		/// supports autoincrement ID?
38
	var $hasAffectedRows = true; 	/// supports affected rows for update/delete?
39
	var $metaTablesSQL = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
40
	var $sysDate = "DATE('now','localtime')";
41
	var $sysTimeStamp = "DATETIME('now','localtime')";
42
	var $fmtTimeStamp = "'Y-m-d H:i:s'";
43
 
44
	/** @var SQLite3 */
45
	var $_connectionID;
46
 
47
	function ServerInfo()
48
	{
49
		$version = SQLite3::version();
50
		$arr['version'] = $version['versionString'];
51
		$arr['description'] = 'SQLite 3';
52
		return $arr;
53
	}
54
 
55
	function BeginTrans()
56
	{
57
		if ($this->transOff) {
58
			return true;
59
		}
60
		$this->Execute("BEGIN TRANSACTION");
61
		$this->transCnt += 1;
62
		return true;
63
	}
64
 
65
	function CommitTrans($ok=true)
66
	{
67
		if ($this->transOff) {
68
			return true;
69
		}
70
		if (!$ok) {
71
			return $this->RollbackTrans();
72
		}
73
		$ret = $this->Execute("COMMIT");
74
		if ($this->transCnt > 0) {
75
			$this->transCnt -= 1;
76
		}
77
		return !empty($ret);
78
	}
79
 
80
	function RollbackTrans()
81
	{
82
		if ($this->transOff) {
83
			return true;
84
		}
85
		$ret = $this->Execute("ROLLBACK");
86
		if ($this->transCnt > 0) {
87
			$this->transCnt -= 1;
88
		}
89
		return !empty($ret);
90
	}
91
 
92
	function metaType($t,$len=-1,$fieldobj=false)
93
	{
94
 
95
		if (is_object($t))
96
		{
97
			$fieldobj = $t;
98
			$t = $fieldobj->type;
99
			$len = $fieldobj->max_length;
100
		}
101
 
102
		$t = strtoupper($t);
103
 
104
		if (array_key_exists($t,$this->customActualTypes))
105
			return  $this->customActualTypes[$t];
106
 
107
		/*
108
		* We are using the Sqlite affinity method here
109
		* @link https://www.sqlite.org/datatype3.html
110
		*/
111
		$affinity = array(
112
		'INT'=>'INTEGER',
113
		'INTEGER'=>'INTEGER',
114
		'TINYINT'=>'INTEGER',
115
		'SMALLINT'=>'INTEGER',
116
		'MEDIUMINT'=>'INTEGER',
117
		'BIGINT'=>'INTEGER',
118
		'UNSIGNED BIG INT'=>'INTEGER',
119
		'INT2'=>'INTEGER',
120
		'INT8'=>'INTEGER',
121
 
122
		'CHARACTER'=>'TEXT',
123
		'VARCHAR'=>'TEXT',
124
		'VARYING CHARACTER'=>'TEXT',
125
		'NCHAR'=>'TEXT',
126
		'NATIVE CHARACTER'=>'TEXT',
127
		'NVARCHAR'=>'TEXT',
128
		'TEXT'=>'TEXT',
129
		'CLOB'=>'TEXT',
130
 
131
		'BLOB'=>'BLOB',
132
 
133
		'REAL'=>'REAL',
134
		'DOUBLE'=>'REAL',
135
		'DOUBLE PRECISION'=>'REAL',
136
		'FLOAT'=>'REAL',
137
 
138
		'NUMERIC'=>'NUMERIC',
139
		'DECIMAL'=>'NUMERIC',
140
		'BOOLEAN'=>'NUMERIC',
141
		'DATE'=>'NUMERIC',
142
		'DATETIME'=>'NUMERIC'
143
		);
144
 
145
		if (!isset($affinity[$t]))
146
			return ADODB_DEFAULT_METATYPE;
147
 
148
		$subt = $affinity[$t];
149
		/*
150
		* Now that we have subclassed the provided data down
151
		* the sqlite 'affinity', we convert to ADOdb metatype
152
		*/
153
 
154
		$subclass = array('INTEGER'=>'I',
155
						  'TEXT'=>'X',
156
						  'BLOB'=>'B',
157
						  'REAL'=>'N',
158
						  'NUMERIC'=>'N');
159
 
160
		return $subclass[$subt];
161
	}
162
	// mark newnham
163
	function MetaColumns($table, $normalize=true)
164
	{
165
		global $ADODB_FETCH_MODE;
166
		$false = false;
167
		$save = $ADODB_FETCH_MODE;
168
		$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
169
		if ($this->fetchMode !== false) {
170
			$savem = $this->SetFetchMode(false);
171
		}
172
		$rs = $this->Execute("PRAGMA table_info('$table')");
173
		if (isset($savem)) {
174
			$this->SetFetchMode($savem);
175
		}
176
		if (!$rs) {
177
			$ADODB_FETCH_MODE = $save;
178
			return $false;
179
		}
180
		$arr = array();
181
		while ($r = $rs->FetchRow()) {
182
			$type = explode('(',$r['type']);
183
			$size = '';
184
			if (sizeof($type)==2) {
185
				$size = trim($type[1],')');
186
			}
187
			$fn = strtoupper($r['name']);
188
			$fld = new ADOFieldObject;
189
			$fld->name = $r['name'];
190
			$fld->type = $type[0];
191
			$fld->max_length = $size;
192
			$fld->not_null = $r['notnull'];
193
			$fld->default_value = $r['dflt_value'];
194
			$fld->scale = 0;
195
			if (isset($r['pk']) && $r['pk']) {
196
				$fld->primary_key=1;
197
			}
198
			if ($save == ADODB_FETCH_NUM) {
199
				$arr[] = $fld;
200
			} else {
201
				$arr[strtoupper($fld->name)] = $fld;
202
			}
203
		}
204
		$rs->Close();
205
		$ADODB_FETCH_MODE = $save;
206
		return $arr;
207
	}
208
 
209
	public function metaForeignKeys($table, $owner = '', $upper =  false, $associative =  false)
210
	{
211
	    global $ADODB_FETCH_MODE;
212
		if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC
213
		|| $this->fetchMode == ADODB_FETCH_ASSOC)
214
		$associative = true;
215
 
216
	    /*
217
		* Read sqlite master to find foreign keys
218
		*/
219
		$sql = "SELECT sql
220
				 FROM (
221
				SELECT sql sql, type type, tbl_name tbl_name, name name
222
				  FROM sqlite_master
223
			          )
224
				WHERE type != 'meta'
225
				  AND sql NOTNULL
226
				  AND LOWER(name) ='" . strtolower($table) . "'";
227
 
228
		$tableSql = $this->getOne($sql);
229
 
230
		$fkeyList = array();
231
		$ylist = preg_split("/,+/",$tableSql);
232
		foreach ($ylist as $y)
233
		{
234
			if (!preg_match('/FOREIGN/',$y))
235
				continue;
236
 
237
			$matches = false;
238
			preg_match_all('/\((.+?)\)/i',$y,$matches);
239
			$tmatches = false;
240
			preg_match_all('/REFERENCES (.+?)\(/i',$y,$tmatches);
241
 
242
			if ($associative)
243
			{
244
				if (!isset($fkeyList[$tmatches[1][0]]))
245
					$fkeyList[$tmatches[1][0]]	= array();
246
				$fkeyList[$tmatches[1][0]][$matches[1][0]] = $matches[1][1];
247
			}
248
			else
249
				$fkeyList[$tmatches[1][0]][] = $matches[1][0] . '=' . $matches[1][1];
250
		}
251
 
252
		if ($associative)
253
		{
254
			if ($upper)
255
				$fkeyList = array_change_key_case($fkeyList,CASE_UPPER);
256
			else
257
				$fkeyList = array_change_key_case($fkeyList,CASE_LOWER);
258
		}
259
		return $fkeyList;
260
	}
261
 
262
 
263
	function _init($parentDriver)
264
	{
265
		$parentDriver->hasTransactions = false;
266
		$parentDriver->hasInsertID = true;
267
	}
268
 
269
	protected function _insertID($table = '', $column = '')
270
	{
271
		return $this->_connectionID->lastInsertRowID();
272
	}
273
 
274
	function _affectedrows()
275
	{
276
		return $this->_connectionID->changes();
277
	}
278
 
279
	function ErrorMsg()
280
 	{
281
		if ($this->_logsql) {
282
			return $this->_errorMsg;
283
		}
284
		return ($this->_errorNo) ? $this->ErrorNo() : ''; //**tochange?
285
	}
286
 
287
	function ErrorNo()
288
	{
289
		return $this->_connectionID->lastErrorCode(); //**tochange??
290
	}
291
 
292
	function SQLDate($fmt, $col=false)
293
	{
294
		/*
295
		* In order to map the values correctly, we must ensure the proper
296
		* casing for certain fields
297
		* Y must be UC, because y is a 2 digit year
298
		* d must be LC, because D is 3 char day
299
		* A must be UC  because a is non-portable am
300
		* Q must be UC  because q means nothing
301
		*/
302
		$fromChars = array('y','D','a','q');
303
		$toChars   = array('Y','d','A','Q');
304
		$fmt       = str_replace($fromChars,$toChars,$fmt);
305
 
306
		$fmt = $this->qstr($fmt);
307
		return ($col) ? "adodb_date2($fmt,$col)" : "adodb_date($fmt)";
308
	}
309
 
310
	function _createFunctions()
311
	{
312
		$this->_connectionID->createFunction('adodb_date', 'adodb_date', 1);
313
		$this->_connectionID->createFunction('adodb_date2', 'adodb_date2', 2);
314
	}
315
 
316
	/** @noinspection PhpUnusedParameterInspection */
317
	function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
318
	{
319
		if (empty($argHostname) && $argDatabasename) {
320
			$argHostname = $argDatabasename;
321
		}
322
		$this->_connectionID = new SQLite3($argHostname);
323
		$this->_createFunctions();
324
 
325
		return true;
326
	}
327
 
328
	function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
329
	{
330
		// There's no permanent connect in SQLite3
331
		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
332
	}
333
 
334
	function _query($sql,$inputarr=false)
335
	{
336
		$rez = $this->_connectionID->query($sql);
337
		if ($rez === false) {
338
			$this->_errorNo = $this->_connectionID->lastErrorCode();
339
		}
340
		// If no data was returned, we don't need to create a real recordset
341
		elseif ($rez->numColumns() == 0) {
342
			$rez->finalize();
343
			$rez = true;
344
		}
345
 
346
		return $rez;
347
	}
348
 
349
	function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0)
350
	{
351
		$nrows = (int) $nrows;
352
		$offset = (int) $offset;
353
		$offsetStr = ($offset >= 0) ? " OFFSET $offset" : '';
354
		$limitStr  = ($nrows >= 0)  ? " LIMIT $nrows" : ($offset >= 0 ? ' LIMIT 999999999' : '');
355
		if ($secs2cache) {
356
			$rs = $this->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr);
357
		} else {
358
			$rs = $this->Execute($sql."$limitStr$offsetStr",$inputarr);
359
		}
360
 
361
		return $rs;
362
	}
363
 
364
	/*
365
		This algorithm is not very efficient, but works even if table locking
366
		is not available.
367
 
368
		Will return false if unable to generate an ID after $MAXLOOPS attempts.
369
	*/
370
	var $_genSeqSQL = "create table %s (id integer)";
371
 
372
	function GenID($seq='adodbseq',$start=1)
373
	{
374
		// if you have to modify the parameter below, your database is overloaded,
375
		// or you need to implement generation of id's yourself!
376
		$MAXLOOPS = 100;
377
		//$this->debug=1;
378
		while (--$MAXLOOPS>=0) {
379
			@($num = $this->GetOne("select id from $seq"));
380
			if ($num === false) {
381
				$this->Execute(sprintf($this->_genSeqSQL ,$seq));
382
				$start -= 1;
383
				$num = '0';
384
				$ok = $this->Execute("insert into $seq values($start)");
385
				if (!$ok) {
386
					return false;
387
				}
388
			}
389
			$this->Execute("update $seq set id=id+1 where id=$num");
390
 
391
			if ($this->affected_rows() > 0) {
392
				$num += 1;
393
				$this->genID = $num;
394
				return $num;
395
			}
396
		}
397
		if ($fn = $this->raiseErrorFn) {
398
			$fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num);
399
		}
400
		return false;
401
	}
402
 
403
	function createSequence($seqname='adodbseq', $startID=1)
404
	{
405
		if (empty($this->_genSeqSQL)) {
406
			return false;
407
		}
408
		$ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
409
		if (!$ok) {
410
			return false;
411
		}
412
		$startID -= 1;
413
		return $this->Execute("insert into $seqname values($startID)");
414
	}
415
 
416
	var $_dropSeqSQL = 'drop table %s';
417
	function DropSequence($seqname = 'adodbseq')
418
	{
419
		if (empty($this->_dropSeqSQL)) {
420
			return false;
421
		}
422
		return $this->Execute(sprintf($this->_dropSeqSQL,$seqname));
423
	}
424
 
425
	// returns true or false
426
	function _close()
427
	{
428
		return $this->_connectionID->close();
429
	}
430
 
431
	function metaIndexes($table, $primary = FALSE, $owner = false)
432
	{
433
		$false = false;
434
		// save old fetch mode
435
		global $ADODB_FETCH_MODE;
436
		$save = $ADODB_FETCH_MODE;
437
		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
438
		if ($this->fetchMode !== FALSE) {
439
			$savem = $this->SetFetchMode(FALSE);
440
		}
441
 
442
		$pragmaData = array();
443
 
444
		/*
445
		* If we want the primary key, we must extract
446
		* it from the table statement, and the pragma
447
		*/
448
		if ($primary)
449
		{
450
			$sql = sprintf('PRAGMA table_info([%s]);',
451
						   strtolower($table)
452
						   );
453
			$pragmaData = $this->getAll($sql);
454
		}
455
 
456
		/*
457
		* Exclude the empty entry for the primary index
458
		*/
459
		$sqlite = "SELECT name,sql
460
					 FROM sqlite_master
461
					WHERE type='index'
462
					  AND sql IS NOT NULL
463
					  AND LOWER(tbl_name)='%s'";
464
 
465
		$SQL = sprintf($sqlite,
466
				     strtolower($table)
467
					 );
468
 
469
		$rs = $this->execute($SQL);
470
 
471
		if (!is_object($rs)) {
472
			if (isset($savem)) {
473
				$this->SetFetchMode($savem);
474
			}
475
			$ADODB_FETCH_MODE = $save;
476
			return $false;
477
		}
478
 
479
		$indexes = array ();
480
 
481
		while ($row = $rs->FetchRow())
482
		{
483
 
484
			if (!isset($indexes[$row[0]])) {
485
				$indexes[$row[0]] = array(
486
					'unique' => preg_match("/unique/i",$row[1]),
487
					'columns' => array()
488
				);
489
			}
490
			/**
491
			 * The index elements appear in the SQL statement
492
			 * in cols[1] between parentheses
493
			 * e.g CREATE UNIQUE INDEX ware_0 ON warehouse (org,warehouse)
494
			 */
495
			preg_match_all('/\((.*)\)/',$row[1],$indexExpression);
496
			$indexes[$row[0]]['columns'] = array_map('trim',explode(',',$indexExpression[1][0]));
497
		}
498
 
499
		if (isset($savem)) {
500
			$this->SetFetchMode($savem);
501
			$ADODB_FETCH_MODE = $save;
502
		}
503
 
504
		/*
505
		* If we want primary, add it here
506
		*/
507
		if ($primary){
508
 
509
			/*
510
			* Check the previously retrieved pragma to search
511
			* with a closure
512
			*/
513
 
514
			$pkIndexData = array('unique'=>1,'columns'=>array());
515
 
516
			$pkCallBack = function ($value, $key) use (&$pkIndexData) {
517
 
518
				/*
519
				* As we iterate the elements check for pk index and sort
520
				*/
521
				if ($value[5] > 0)
522
				{
523
					$pkIndexData['columns'][$value[5]] = strtolower($value[1]);
524
					ksort($pkIndexData['columns']);
525
				}
526
			};
527
 
528
			array_walk($pragmaData,$pkCallBack);
529
 
530
			/*
531
			* If we found no columns, there is no
532
			* primary index
533
			*/
534
			if (count($pkIndexData['columns']) > 0)
535
				$indexes['PRIMARY'] = $pkIndexData;
536
		}
537
 
538
		return $indexes;
539
	}
540
 
541
	/**
542
	* Returns the maximum size of a MetaType C field. Because of the
543
	* database design, sqlite places no limits on the size of data inserted
544
	*
545
	* @return int
546
	*/
547
	function charMax()
548
	{
549
		return ADODB_STRINGMAX_NOLIMIT;
550
	}
551
 
552
	/**
553
	* Returns the maximum size of a MetaType X field. Because of the
554
	* database design, sqlite places no limits on the size of data inserted
555
	*
556
	* @return int
557
	*/
558
	function textMax()
559
	{
560
		return ADODB_STRINGMAX_NOLIMIT;
561
	}
562
 
563
	/**
564
	 * Converts a date to a month only field and pads it to 2 characters
565
	 *
566
	 * This uses the more efficient strftime native function to process
567
	 *
568
	 * @param string $fld	The name of the field to process
569
	 *
570
	 * @return string The SQL Statement
571
	 */
572
	function month($fld)
573
	{
574
		return "strftime('%m',$fld)";
575
	}
576
 
577
	/**
578
	 * Converts a date to a day only field and pads it to 2 characters
579
	 *
580
	 * This uses the more efficient strftime native function to process
581
	 *
582
	 * @param string $fld	The name of the field to process
583
	 *
584
	 * @return string The SQL Statement
585
	 */
586
	function day($fld) {
587
		return "strftime('%d',$fld)";
588
	}
589
 
590
	/**
591
	 * Converts a date to a year only field
592
	 *
593
	 * This uses the more efficient strftime native function to process
594
	 *
595
	 * @param string $fld	The name of the field to process
596
	 *
597
	 * @return string The SQL Statement
598
	 */
599
	function year($fld)
600
	{
601
		return "strftime('%Y',$fld)";
602
	}
603
 
604
	/**
605
	 * SQLite update for blob
606
	 *
607
	 * SQLite must be a fully prepared statement (all variables must be bound),
608
	 * so $where can either be an array (array params) or a string that we will
609
	 * do our best to unpack and turn into a prepared statement.
610
	 *
611
	 * @param string $table
612
	 * @param string $column
613
	 * @param string $val      Blob value to set
614
	 * @param mixed  $where    An array of parameters (key => value pairs),
615
	 *                         or a string (where clause).
616
	 * @param string $blobtype ignored
617
	 *
618
	 * @return bool success
619
	 */
620
	function updateBlob($table, $column, $val, $where, $blobtype = 'BLOB')
621
	{
622
		if (is_array($where)) {
623
			// We were passed a set of key=>value pairs
624
			$params = $where;
625
		} else {
626
			// Given a where clause string, we have to disassemble the
627
			// statements into keys and values
628
			$params = array();
629
			$temp = preg_split('/(where|and)/i', $where);
630
			$where = array_filter($temp);
631
 
632
			foreach ($where as $wValue) {
633
				$wTemp = preg_split('/[= \']+/', $wValue);
634
				$wTemp = array_filter($wTemp);
635
				$wTemp = array_values($wTemp);
636
				$params[$wTemp[0]] = $wTemp[1];
637
			}
638
		}
639
 
640
		$paramWhere = array();
641
		foreach ($params as $bindKey => $bindValue) {
642
			$paramWhere[] = $bindKey . '=?';
643
		}
644
 
645
		$sql = "UPDATE $table SET $column=? WHERE "
646
			. implode(' AND ', $paramWhere);
647
 
648
		// Prepare the statement
649
		$stmt = $this->_connectionID->prepare($sql);
650
 
651
		// Set the first bind value equal to value we want to update
652
		if (!$stmt->bindValue(1, $val, SQLITE3_BLOB)) {
653
			return false;
654
		}
655
 
656
		// Build as many keys as available
657
		$bindIndex = 2;
658
		foreach ($params as $bindValue) {
659
			if (is_integer($bindValue) || is_bool($bindValue) || is_float($bindValue)) {
660
				$type = SQLITE3_NUM;
661
			} elseif (is_object($bindValue)) {
662
				// Assume a blob, this should never appear in
663
				// the binding for a where statement anyway
664
				$type = SQLITE3_BLOB;
665
			} else {
666
				$type = SQLITE3_TEXT;
667
			}
668
 
669
			if (!$stmt->bindValue($bindIndex, $bindValue, $type)) {
670
				return false;
671
			}
672
 
673
			$bindIndex++;
674
		}
675
 
676
		// Now execute the update. NB this is SQLite execute, not ADOdb
677
		$ok = $stmt->execute();
678
		return is_object($ok);
679
	}
680
 
681
	/**
682
	 * SQLite update for blob from a file
683
	 *
684
	 * @param string $table
685
	 * @param string $column
686
	 * @param string $path      Filename containing blob data
687
	 * @param mixed  $where    {@see updateBlob()}
688
	 * @param string $blobtype ignored
689
	 *
690
	 * @return bool success
691
	 */
692
	function updateBlobFile($table, $column, $path, $where, $blobtype = 'BLOB')
693
	{
694
		if (!file_exists($path)) {
695
			return false;
696
		}
697
 
698
		// Read file information
699
		$fileContents = file_get_contents($path);
700
		if ($fileContents === false)
701
			// Distinguish between an empty file and failure
702
			return false;
703
 
704
		return $this->updateBlob($table, $column, $fileContents, $where, $blobtype);
705
	}
706
 
707
}
708
 
709
/*--------------------------------------------------------------------------------------
710
		Class Name: Recordset
711
--------------------------------------------------------------------------------------*/
712
 
713
class ADORecordset_sqlite3 extends ADORecordSet {
714
 
715
	var $databaseType = "sqlite3";
716
	var $bind = false;
717
 
718
	/** @var SQLite3Result */
719
	var $_queryID;
720
 
721
	/** @noinspection PhpMissingParentConstructorInspection */
722
	function __construct($queryID,$mode=false)
723
	{
724
		if ($mode === false) {
725
			global $ADODB_FETCH_MODE;
726
			$mode = $ADODB_FETCH_MODE;
727
		}
728
		switch($mode) {
729
			case ADODB_FETCH_NUM:
730
				$this->fetchMode = SQLITE3_NUM;
731
				break;
732
			case ADODB_FETCH_ASSOC:
733
				$this->fetchMode = SQLITE3_ASSOC;
734
				break;
735
			default:
736
				$this->fetchMode = SQLITE3_BOTH;
737
				break;
738
		}
739
		$this->adodbFetchMode = $mode;
740
 
741
		$this->_queryID = $queryID;
742
 
743
		$this->_inited = true;
744
		$this->fields = array();
745
		if ($queryID) {
746
			$this->_currentRow = 0;
747
			$this->EOF = !$this->_fetch();
748
			@$this->_initrs();
749
		} else {
750
			$this->_numOfRows = 0;
751
			$this->_numOfFields = 0;
752
			$this->EOF = true;
753
		}
754
 
755
		return $this->_queryID;
756
	}
757
 
758
 
759
	function FetchField($fieldOffset = -1)
760
	{
761
		$fld = new ADOFieldObject;
762
		$fld->name = $this->_queryID->columnName($fieldOffset);
763
		$fld->type = 'VARCHAR';
764
		$fld->max_length = -1;
765
		return $fld;
766
	}
767
 
768
	function _initrs()
769
	{
770
		$this->_numOfFields = $this->_queryID->numColumns();
771
 
772
	}
773
 
774
	function Fields($colname)
775
	{
776
		if ($this->fetchMode != SQLITE3_NUM) {
777
			return $this->fields[$colname];
778
		}
779
		if (!$this->bind) {
780
			$this->bind = array();
781
			for ($i=0; $i < $this->_numOfFields; $i++) {
782
				$o = $this->FetchField($i);
783
				$this->bind[strtoupper($o->name)] = $i;
784
			}
785
		}
786
 
787
		return $this->fields[$this->bind[strtoupper($colname)]];
788
	}
789
 
790
	function _seek($row)
791
	{
792
		// sqlite3 does not implement seek
793
		if ($this->debug) {
794
			ADOConnection::outp("SQLite3 does not implement seek");
795
		}
796
		return false;
797
	}
798
 
799
	function _fetch($ignore_fields=false)
800
	{
801
		$this->fields = $this->_queryID->fetchArray($this->fetchMode);
802
		return !empty($this->fields);
803
	}
804
 
805
	function _close()
806
	{
807
	}
808
 
809
}