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