Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
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 class ADODB_sqlite3 extends ADOConnection { 28 var $databaseType = "sqlite3"; 29 var $dataProvider = "sqlite"; 30 var $replaceQuote = "''"; // string to use to replace quotes 31 var $concat_operator='||'; 32 var $_errorNo = 0; 33 var $hasLimit = true; 34 var $hasInsertID = true; /// supports autoincrement ID? 35 var $hasAffectedRows = true; /// supports affected rows for update/delete? 36 var $metaTablesSQL = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"; 37 var $sysDate = "adodb_date('Y-m-d')"; 38 var $sysTimeStamp = "adodb_date('Y-m-d H:i:s')"; 39 var $fmtTimeStamp = "'Y-m-d H:i:s'"; 40 41 function ServerInfo() 42 { 43 $version = SQLite3::version(); 44 $arr['version'] = $version['versionString']; 45 $arr['description'] = 'SQLite 3'; 46 return $arr; 47 } 48 49 function BeginTrans() 50 { 51 if ($this->transOff) { 52 return true; 53 } 54 $ret = $this->Execute("BEGIN TRANSACTION"); 55 $this->transCnt += 1; 56 return true; 57 } 58 59 function CommitTrans($ok=true) 60 { 61 if ($this->transOff) { 62 return true; 63 } 64 if (!$ok) { 65 return $this->RollbackTrans(); 66 } 67 $ret = $this->Execute("COMMIT"); 68 if ($this->transCnt > 0) { 69 $this->transCnt -= 1; 70 } 71 return !empty($ret); 72 } 73 74 function RollbackTrans() 75 { 76 if ($this->transOff) { 77 return true; 78 } 79 $ret = $this->Execute("ROLLBACK"); 80 if ($this->transCnt > 0) { 81 $this->transCnt -= 1; 82 } 83 return !empty($ret); 84 } 85 86 function metaType($t,$len=-1,$fieldobj=false) 87 { 88 89 if (is_object($t)) 90 { 91 $fieldobj = $t; 92 $t = $fieldobj->type; 93 $len = $fieldobj->max_length; 94 } 95 96 $t = strtoupper($t); 97 98 /* 99 * We are using the Sqlite affinity method here 100 * @link https://www.sqlite.org/datatype3.html 101 */ 102 $affinity = array( 103 'INT'=>'INTEGER', 104 'INTEGER'=>'INTEGER', 105 'TINYINT'=>'INTEGER', 106 'SMALLINT'=>'INTEGER', 107 'MEDIUMINT'=>'INTEGER', 108 'BIGINT'=>'INTEGER', 109 'UNSIGNED BIG INT'=>'INTEGER', 110 'INT2'=>'INTEGER', 111 'INT8'=>'INTEGER', 112 113 'CHARACTER'=>'TEXT', 114 'VARCHAR'=>'TEXT', 115 'VARYING CHARACTER'=>'TEXT', 116 'NCHAR'=>'TEXT', 117 'NATIVE CHARACTER'=>'TEXT', 118 'NVARCHAR'=>'TEXT', 119 'TEXT'=>'TEXT', 120 'CLOB'=>'TEXT', 121 122 'BLOB'=>'BLOB', 123 124 'REAL'=>'REAL', 125 'DOUBLE'=>'REAL', 126 'DOUBLE PRECISION'=>'REAL', 127 'FLOAT'=>'REAL', 128 129 'NUMERIC'=>'NUMERIC', 130 'DECIMAL'=>'NUMERIC', 131 'BOOLEAN'=>'NUMERIC', 132 'DATE'=>'NUMERIC', 133 'DATETIME'=>'NUMERIC' 134 ); 135 136 if (!isset($affinity[$t])) 137 return ADODB_DEFAULT_METATYPE; 138 139 $subt = $affinity[$t]; 140 /* 141 * Now that we have subclassed the provided data down 142 * the sqlite 'affinity', we convert to ADOdb metatype 143 */ 144 145 $subclass = array('INTEGER'=>'I', 146 'TEXT'=>'X', 147 'BLOB'=>'B', 148 'REAL'=>'N', 149 'NUMERIC'=>'N'); 150 151 return $subclass[$subt]; 152 } 153 // mark newnham 154 function MetaColumns($table, $normalize=true) 155 { 156 global $ADODB_FETCH_MODE; 157 $false = false; 158 $save = $ADODB_FETCH_MODE; 159 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC; 160 if ($this->fetchMode !== false) { 161 $savem = $this->SetFetchMode(false); 162 } 163 $rs = $this->Execute("PRAGMA table_info('$table')"); 164 if (isset($savem)) { 165 $this->SetFetchMode($savem); 166 } 167 if (!$rs) { 168 $ADODB_FETCH_MODE = $save; 169 return $false; 170 } 171 $arr = array(); 172 while ($r = $rs->FetchRow()) { 173 $type = explode('(',$r['type']); 174 $size = ''; 175 if (sizeof($type)==2) { 176 $size = trim($type[1],')'); 177 } 178 $fn = strtoupper($r['name']); 179 $fld = new ADOFieldObject; 180 $fld->name = $r['name']; 181 $fld->type = $type[0]; 182 $fld->max_length = $size; 183 $fld->not_null = $r['notnull']; 184 $fld->default_value = $r['dflt_value']; 185 $fld->scale = 0; 186 if (isset($r['pk']) && $r['pk']) { 187 $fld->primary_key=1; 188 } 189 if ($save == ADODB_FETCH_NUM) { 190 $arr[] = $fld; 191 } else { 192 $arr[strtoupper($fld->name)] = $fld; 193 } 194 } 195 $rs->Close(); 196 $ADODB_FETCH_MODE = $save; 197 return $arr; 198 } 199 200 function metaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) 201 { 202 global $ADODB_FETCH_MODE; 203 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC 204 || $this->fetchMode == ADODB_FETCH_ASSOC) 205 $associative = true; 206 207 /* 208 * Read sqlite master to find foreign keys 209 */ 210 $sql = "SELECT sql 211 FROM ( 212 SELECT sql sql, type type, tbl_name tbl_name, name name 213 FROM sqlite_master 214 ) 215 WHERE type != 'meta' 216 AND sql NOTNULL 217 AND LOWER(name) ='" . strtolower($table) . "'"; 218 219 $tableSql = $this->getOne($sql); 220 221 $fkeyList = array(); 222 $ylist = preg_split("/,+/",$tableSql); 223 foreach ($ylist as $y) 224 { 225 if (!preg_match('/FOREIGN/',$y)) 226 continue; 227 228 $matches = false; 229 preg_match_all('/\((.+?)\)/i',$y,$matches); 230 $tmatches = false; 231 preg_match_all('/REFERENCES (.+?)\(/i',$y,$tmatches); 232 233 if ($associative) 234 { 235 if (!isset($fkeyList[$tmatches[1][0]])) 236 $fkeyList[$tmatches[1][0]] = array(); 237 $fkeyList[$tmatches[1][0]][$matches[1][0]] = $matches[1][1]; 238 } 239 else 240 $fkeyList[$tmatches[1][0]][] = $matches[1][0] . '=' . $matches[1][1]; 241 } 242 243 if ($associative) 244 { 245 if ($upper) 246 $fkeyList = array_change_key_case($fkeyList,CASE_UPPER); 247 else 248 $fkeyList = array_change_key_case($fkeyList,CASE_LOWER); 249 } 250 return $fkeyList; 251 } 252 253 254 function _init($parentDriver) 255 { 256 $parentDriver->hasTransactions = false; 257 $parentDriver->hasInsertID = true; 258 } 259 260 protected function _insertID($table = '', $column = '') 261 { 262 return $this->_connectionID->lastInsertRowID(); 263 } 264 265 function _affectedrows() 266 { 267 return $this->_connectionID->changes(); 268 } 269 270 function ErrorMsg() 271 { 272 if ($this->_logsql) { 273 return $this->_errorMsg; 274 } 275 return ($this->_errorNo) ? $this->ErrorNo() : ''; //**tochange? 276 } 277 278 function ErrorNo() 279 { 280 return $this->_connectionID->lastErrorCode(); //**tochange?? 281 } 282 283 function SQLDate($fmt, $col=false) 284 { 285 /* 286 * In order to map the values correctly, we must ensure the proper 287 * casing for certain fields 288 * Y must be UC, because y is a 2 digit year 289 * d must be LC, because D is 3 char day 290 * A must be UC because a is non-portable am 291 * Q must be UC because q means nothing 292 */ 293 $fromChars = array('y','D','a','q'); 294 $toChars = array('Y','d','A','Q'); 295 $fmt = str_replace($fromChars,$toChars,$fmt); 296 297 $fmt = $this->qstr($fmt); 298 return ($col) ? "adodb_date2($fmt,$col)" : "adodb_date($fmt)"; 299 } 300 301 function _createFunctions() 302 { 303 $this->_connectionID->createFunction('adodb_date', 'adodb_date', 1); 304 $this->_connectionID->createFunction('adodb_date2', 'adodb_date2', 2); 305 } 306 307 308 // returns true or false 309 function _connect($argHostname, $argUsername, $argPassword, $argDatabasename) 310 { 311 if (empty($argHostname) && $argDatabasename) { 312 $argHostname = $argDatabasename; 313 } 314 $this->_connectionID = new SQLite3($argHostname); 315 $this->_createFunctions(); 316 317 return true; 318 } 319 320 // returns true or false 321 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) 322 { 323 // There's no permanent connect in SQLite3 324 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); 325 } 326 327 // returns query ID if successful, otherwise false 328 function _query($sql,$inputarr=false) 329 { 330 $rez = $this->_connectionID->query($sql); 331 if ($rez === false) { 332 $this->_errorNo = $this->_connectionID->lastErrorCode(); 333 } 334 // If no data was returned, we don't need to create a real recordset 335 elseif ($rez->numColumns() == 0) { 336 $rez->finalize(); 337 $rez = true; 338 } 339 340 return $rez; 341 } 342 343 function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0) 344 { 345 $nrows = (int) $nrows; 346 $offset = (int) $offset; 347 $offsetStr = ($offset >= 0) ? " OFFSET $offset" : ''; 348 $limitStr = ($nrows >= 0) ? " LIMIT $nrows" : ($offset >= 0 ? ' LIMIT 999999999' : ''); 349 if ($secs2cache) { 350 $rs = $this->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr); 351 } else { 352 $rs = $this->Execute($sql."$limitStr$offsetStr",$inputarr); 353 } 354 355 return $rs; 356 } 357 358 /* 359 This algorithm is not very efficient, but works even if table locking 360 is not available. 361 362 Will return false if unable to generate an ID after $MAXLOOPS attempts. 363 */ 364 var $_genSeqSQL = "create table %s (id integer)"; 365 366 function GenID($seq='adodbseq',$start=1) 367 { 368 // if you have to modify the parameter below, your database is overloaded, 369 // or you need to implement generation of id's yourself! 370 $MAXLOOPS = 100; 371 //$this->debug=1; 372 while (--$MAXLOOPS>=0) { 373 @($num = $this->GetOne("select id from $seq")); 374 if ($num === false) { 375 $this->Execute(sprintf($this->_genSeqSQL ,$seq)); 376 $start -= 1; 377 $num = '0'; 378 $ok = $this->Execute("insert into $seq values($start)"); 379 if (!$ok) { 380 return false; 381 } 382 } 383 $this->Execute("update $seq set id=id+1 where id=$num"); 384 385 if ($this->affected_rows() > 0) { 386 $num += 1; 387 $this->genID = $num; 388 return $num; 389 } 390 } 391 if ($fn = $this->raiseErrorFn) { 392 $fn($this->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num); 393 } 394 return false; 395 } 396 397 function CreateSequence($seqname='adodbseq',$start=1) 398 { 399 if (empty($this->_genSeqSQL)) { 400 return false; 401 } 402 $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); 403 if (!$ok) { 404 return false; 405 } 406 $start -= 1; 407 return $this->Execute("insert into $seqname values($start)"); 408 } 409 410 var $_dropSeqSQL = 'drop table %s'; 411 function DropSequence($seqname = 'adodbseq') 412 { 413 if (empty($this->_dropSeqSQL)) { 414 return false; 415 } 416 return $this->Execute(sprintf($this->_dropSeqSQL,$seqname)); 417 } 418 419 // returns true or false 420 function _close() 421 { 422 return $this->_connectionID->close(); 423 } 424 425 function metaIndexes($table, $primary = FALSE, $owner = false) 426 { 427 $false = false; 428 // save old fetch mode 429 global $ADODB_FETCH_MODE; 430 $save = $ADODB_FETCH_MODE; 431 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 432 if ($this->fetchMode !== FALSE) { 433 $savem = $this->SetFetchMode(FALSE); 434 } 435 436 $pragmaData = array(); 437 438 /* 439 * If we want the primary key, we must extract 440 * it from the table statement, and the pragma 441 */ 442 if ($primary) 443 { 444 $sql = sprintf('PRAGMA table_info([%s]);', 445 strtolower($table) 446 ); 447 $pragmaData = $this->getAll($sql); 448 } 449 450 /* 451 * Exclude the empty entry for the primary index 452 */ 453 $sqlite = "SELECT name,sql 454 FROM sqlite_master 455 WHERE type='index' 456 AND sql IS NOT NULL 457 AND LOWER(tbl_name)='%s'"; 458 459 $SQL = sprintf($sqlite, 460 strtolower($table) 461 ); 462 463 $rs = $this->execute($SQL); 464 465 if (!is_object($rs)) { 466 if (isset($savem)) { 467 $this->SetFetchMode($savem); 468 } 469 $ADODB_FETCH_MODE = $save; 470 return $false; 471 } 472 473 $indexes = array (); 474 475 while ($row = $rs->FetchRow()) 476 { 477 478 if (!isset($indexes[$row[0]])) { 479 $indexes[$row[0]] = array( 480 'unique' => preg_match("/unique/i",$row[1]), 481 'columns' => array() 482 ); 483 } 484 /** 485 * The index elements appear in the SQL statement 486 * in cols[1] between parentheses 487 * e.g CREATE UNIQUE INDEX ware_0 ON warehouse (org,warehouse) 488 */ 489 preg_match_all('/\((.*)\)/',$row[1],$indexExpression); 490 $indexes[$row[0]]['columns'] = array_map('trim',explode(',',$indexExpression[1][0])); 491 } 492 493 if (isset($savem)) { 494 $this->SetFetchMode($savem); 495 $ADODB_FETCH_MODE = $save; 496 } 497 498 /* 499 * If we want primary, add it here 500 */ 501 if ($primary){ 502 503 /* 504 * Check the previously retrieved pragma to search 505 * with a closure 506 */ 507 508 $pkIndexData = array('unique'=>1,'columns'=>array()); 509 510 $pkCallBack = function ($value, $key) use (&$pkIndexData) { 511 512 /* 513 * As we iterate the elements check for pk index and sort 514 */ 515 if ($value[5] > 0) 516 { 517 $pkIndexData['columns'][$value[5]] = strtolower($value[1]); 518 ksort($pkIndexData['columns']); 519 } 520 }; 521 522 array_walk($pragmaData,$pkCallBack); 523 524 /* 525 * If we found no columns, there is no 526 * primary index 527 */ 528 if (count($pkIndexData['columns']) > 0) 529 $indexes['PRIMARY'] = $pkIndexData; 530 } 531 532 return $indexes; 533 } 534 535 /** 536 * Returns the maximum size of a MetaType C field. Because of the 537 * database design, sqlite places no limits on the size of data inserted 538 * 539 * @return int 540 */ 541 function charMax() 542 { 543 return ADODB_STRINGMAX_NOLIMIT; 544 } 545 546 /** 547 * Returns the maximum size of a MetaType X field. Because of the 548 * database design, sqlite places no limits on the size of data inserted 549 * 550 * @return int 551 */ 552 function textMax() 553 { 554 return ADODB_STRINGMAX_NOLIMIT; 555 } 556 557 /** 558 * Converts a date to a month only field and pads it to 2 characters 559 * 560 * This uses the more efficient strftime native function to process 561 * 562 * @param str $fld The name of the field to process 563 * 564 * @return str The SQL Statement 565 */ 566 function month($fld) 567 { 568 $x = "strftime('%m',$fld)"; 569 return $x; 570 } 571 572 /** 573 * Converts a date to a day only field and pads it to 2 characters 574 * 575 * This uses the more efficient strftime native function to process 576 * 577 * @param str $fld The name of the field to process 578 * 579 * @return str The SQL Statement 580 */ 581 function day($fld) { 582 $x = "strftime('%d',$fld)"; 583 return $x; 584 } 585 586 /** 587 * Converts a date to a year only field 588 * 589 * This uses the more efficient strftime native function to process 590 * 591 * @param str $fld The name of the field to process 592 * 593 * @return str The SQL Statement 594 */ 595 function year($fld) 596 { 597 $x = "strftime('%Y',$fld)"; 598 return $x; 599 } 600 601 } 602 603 /*-------------------------------------------------------------------------------------- 604 Class Name: Recordset 605 --------------------------------------------------------------------------------------*/ 606 607 class ADORecordset_sqlite3 extends ADORecordSet { 608 609 var $databaseType = "sqlite3"; 610 var $bind = false; 611 612 function __construct($queryID,$mode=false) 613 { 614 615 if ($mode === false) { 616 global $ADODB_FETCH_MODE; 617 $mode = $ADODB_FETCH_MODE; 618 } 619 switch($mode) { 620 case ADODB_FETCH_NUM: 621 $this->fetchMode = SQLITE3_NUM; 622 break; 623 case ADODB_FETCH_ASSOC: 624 $this->fetchMode = SQLITE3_ASSOC; 625 break; 626 default: 627 $this->fetchMode = SQLITE3_BOTH; 628 break; 629 } 630 $this->adodbFetchMode = $mode; 631 632 $this->_queryID = $queryID; 633 634 $this->_inited = true; 635 $this->fields = array(); 636 if ($queryID) { 637 $this->_currentRow = 0; 638 $this->EOF = !$this->_fetch(); 639 @$this->_initrs(); 640 } else { 641 $this->_numOfRows = 0; 642 $this->_numOfFields = 0; 643 $this->EOF = true; 644 } 645 646 return $this->_queryID; 647 } 648 649 650 function FetchField($fieldOffset = -1) 651 { 652 $fld = new ADOFieldObject; 653 $fld->name = $this->_queryID->columnName($fieldOffset); 654 $fld->type = 'VARCHAR'; 655 $fld->max_length = -1; 656 return $fld; 657 } 658 659 function _initrs() 660 { 661 $this->_numOfFields = $this->_queryID->numColumns(); 662 663 } 664 665 function Fields($colname) 666 { 667 if ($this->fetchMode != SQLITE3_NUM) { 668 return $this->fields[$colname]; 669 } 670 if (!$this->bind) { 671 $this->bind = array(); 672 for ($i=0; $i < $this->_numOfFields; $i++) { 673 $o = $this->FetchField($i); 674 $this->bind[strtoupper($o->name)] = $i; 675 } 676 } 677 678 return $this->fields[$this->bind[strtoupper($colname)]]; 679 } 680 681 function _seek($row) 682 { 683 // sqlite3 does not implement seek 684 if ($this->debug) { 685 ADOConnection::outp("SQLite3 does not implement seek"); 686 } 687 return false; 688 } 689 690 function _fetch($ignore_fields=false) 691 { 692 $this->fields = $this->_queryID->fetchArray($this->fetchMode); 693 return !empty($this->fields); 694 } 695 696 function _close() 697 { 698 } 699 700 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body