PageRenderTime 82ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/system/libraries/drivers/Database/Pdosqlite.php

https://github.com/lmorchard/friendfeedarchiver
PHP | 670 lines | 535 code | 42 blank | 93 comment | 20 complexity | 12c64229020de2adcb38aedafff1472c MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /*
  3. * Class: Database_PdoSqlite_Driver
  4. * Provides specific database items for Sqlite.
  5. *
  6. * Connection string should be, eg: "pdosqlite://path/to/database.db"
  7. *
  8. * Version 1.0 alpha
  9. * author - Doutu, updated by gregmac
  10. * copyright - (c) BSD
  11. * license - <no>
  12. */
  13. class Database_Pdosqlite_Driver extends Database_Driver {
  14. // Database connection link
  15. protected $link;
  16. protected $db_config;
  17. /*
  18. * Constructor: __construct
  19. * Sets up the config for the class.
  20. *
  21. * Parameters:
  22. * config - database configuration
  23. *
  24. */
  25. public function __construct($config)
  26. {
  27. $this->db_config = $config;
  28. Log::add('debug', 'PDO:Sqlite Database Driver Initialized');
  29. }
  30. public function connect()
  31. {
  32. // Import the connect variables
  33. extract($this->db_config['connection']);
  34. try
  35. {
  36. $this->link = ($this->db_config['persistent'] == TRUE)
  37. ? new PDO ('sqlite:'.$socket.$database, $user, $pass, array(PDO::ATTR_PERSISTENT => true))
  38. : new PDO ('sqlite:'.$socket.$database, $user, $pass);
  39. $this->link->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);
  40. $this->link->query('PRAGMA count_changes=1;');
  41. if ($charset = $this->db_config['character_set'])
  42. {
  43. $this->set_charset($charset);
  44. }
  45. }
  46. catch (PDOException $e)
  47. {
  48. throw new Kohana_Database_Exception('database.error', $e->getMessage());
  49. }
  50. // Clear password after successful connect
  51. $this->db_config['connection']['pass'] = NULL;
  52. return $this->link;
  53. }
  54. public function query($sql)
  55. {
  56. try
  57. {
  58. $sth = $this->link->prepare($sql);
  59. }
  60. catch (PDOException $e)
  61. {
  62. throw new Kohana_Database_Exception('database.error', $e->getMessage());
  63. }
  64. return new Pdosqlite_Result($sth, $this->link, $this->db_config['object'], $sql);
  65. }
  66. public function set_charset($charset)
  67. {
  68. $this->link->query('PRAGMA encoding = '.$this->escape_str($charset));
  69. }
  70. public function escape_table($table)
  71. {
  72. return '`'.str_replace('.', '`.`', $table).'`';
  73. }
  74. public function escape_column($column)
  75. {
  76. if (strtolower($column) == 'count(*)' OR $column == '*')
  77. return $column;
  78. // This matches any modifiers we support to SELECT.
  79. if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column))
  80. {
  81. if (stripos($column, ' AS ') !== FALSE)
  82. {
  83. // Force 'AS' to uppercase
  84. $column = str_ireplace(' AS ', ' AS ', $column);
  85. // Runs escape_column on both sides of an AS statement
  86. $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column));
  87. // Re-create the AS statement
  88. return implode(' AS ', $column);
  89. }
  90. return preg_replace('/[^.*]+/', '`$0`', $column);
  91. }
  92. $parts = explode(' ', $column);
  93. $column = '';
  94. for ($i = 0, $c = count($parts); $i < $c; $i++)
  95. {
  96. // The column is always last
  97. if ($i == ($c - 1))
  98. {
  99. $column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]);
  100. }
  101. else // otherwise, it's a modifier
  102. {
  103. $column .= $parts[$i].' ';
  104. }
  105. }
  106. return $column;
  107. }
  108. public function regex($field, $match = '', $type = 'AND ', $num_regexs)
  109. {
  110. throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
  111. }
  112. public function notregex($field, $match = '', $type = 'AND ', $num_regexs)
  113. {
  114. throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
  115. }
  116. public function merge($table, $keys, $values)
  117. {
  118. throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
  119. }
  120. public function limit($limit, $offset = 0)
  121. {
  122. return 'LIMIT '.$offset.', '.$limit;
  123. }
  124. public function stmt_prepare($sql = '')
  125. {
  126. throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
  127. }
  128. public function compile_select($database)
  129. {
  130. $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT ';
  131. $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*';
  132. if (count($database['from']) > 0)
  133. {
  134. $sql .= "\nFROM ";
  135. $sql .= implode(', ', $database['from']);
  136. }
  137. if (count($database['join']) > 0)
  138. {
  139. $sql .= ' '.implode("\n", $database['join']);
  140. }
  141. if (count($database['where']) > 0)
  142. {
  143. $sql .= "\nWHERE ";
  144. }
  145. $sql .= implode("\n", $database['where']);
  146. if (count($database['groupby']) > 0)
  147. {
  148. $sql .= "\nGROUP BY ";
  149. $sql .= implode(', ', $database['groupby']);
  150. }
  151. if (count($database['having']) > 0)
  152. {
  153. $sql .= "\nHAVING ";
  154. $sql .= implode("\n", $database['having']);
  155. }
  156. if (count($database['orderby']) > 0)
  157. {
  158. $sql .= "\nORDER BY ";
  159. $sql .= implode(', ', $database['orderby']);
  160. }
  161. if (is_numeric($database['limit']))
  162. {
  163. $sql .= "\n";
  164. $sql .= $this->limit($database['limit'], $database['offset']);
  165. }
  166. return $sql;
  167. }
  168. public function escape_str($str)
  169. {
  170. if(function_exists('sqlite_escape_string'))
  171. {
  172. $res = sqlite_escape_string($str);
  173. }
  174. else
  175. {
  176. $res = str_replace("'", "''", $str);
  177. }
  178. return $res;
  179. }
  180. public function list_tables()
  181. {
  182. $sql = "SELECT `name` FROM `sqlite_master` WHERE `type`='table' ORDER BY `name`;";
  183. try
  184. {
  185. $result = $this->query($sql)->result(FALSE, PDO::FETCH_ASSOC);
  186. $retval = array();
  187. foreach($result as $row)
  188. {
  189. $retval[] = current($row);
  190. }
  191. }
  192. catch (PDOException $e)
  193. {
  194. throw new Kohana_Database_Exception('database.error', $e->getMessage());
  195. }
  196. return $retval;
  197. }
  198. public function show_error()
  199. {
  200. $err = $this->link->errorInfo();
  201. return isset($err[2]) ? $err[2] : 'Unknown error!';
  202. }
  203. public function list_fields($table, $query = FALSE)
  204. {
  205. static $tables;
  206. if (is_object($query))
  207. {
  208. if (empty($tables[$table]))
  209. {
  210. $tables[$table] = array();
  211. foreach($query->result() as $row)
  212. {
  213. $tables[$table][] = $row->name;
  214. }
  215. }
  216. return $tables[$table];
  217. }
  218. else
  219. {
  220. $result = $this->link->query( 'PRAGMA table_info('.$this->escape_table($table).')' );
  221. foreach($result as $row)
  222. {
  223. $tables[$table][$row['name']] = $this->sql_type($row['type']);
  224. }
  225. return $tables[$table];
  226. }
  227. }
  228. public function field_data($table)
  229. {
  230. Log::add('error', 'This method is under developing');
  231. }
  232. /**
  233. * Version number query string
  234. *
  235. * @access public
  236. * @return string
  237. */
  238. function version()
  239. {
  240. return $this->link->getAttribute(constant("PDO::ATTR_SERVER_VERSION"));
  241. }
  242. } // End Database_PdoSqlite_Driver Class
  243. /*
  244. * Class: PdoSqlite_Result
  245. * The result class for Sqlite queries.
  246. *
  247. * Version 1.0 alpha
  248. * author - Doutu, updated by gregmac
  249. * copyright - (c) BSD
  250. * license - <no>
  251. */
  252. class Pdosqlite_Result implements Database_Result, ArrayAccess, Iterator, Countable {
  253. // Result resource
  254. protected $result = NULL;
  255. // Total rows and current row
  256. protected $total_rows = FALSE;
  257. protected $current_row = FALSE;
  258. // Insert id
  259. protected $insert_id = FALSE;
  260. // Data fetching types
  261. protected $fetch_type = PDO::FETCH_OBJ;
  262. protected $return_type = PDO::FETCH_ASSOC;
  263. /*
  264. * Constructor: __construct
  265. * Sets up the class.
  266. *
  267. * Parameters:
  268. * result - result resource
  269. * link - database resource link
  270. * object - return objects or arrays
  271. * sql - sql query that was run
  272. *
  273. */
  274. public function __construct($result, $link, $object = TRUE, $sql)
  275. {
  276. if (is_object($result) OR $result = $link->prepare($sql))
  277. {
  278. // run the query
  279. try
  280. {
  281. $result->execute();
  282. }
  283. catch (PDOException $e)
  284. {
  285. throw new Kohana_Database_Exception('database.error', $e->getMessage());
  286. }
  287. if (preg_match('/^SELECT|PRAGMA|EXPLAIN/i', $sql))
  288. {
  289. //Log::add('debug','it was a SELECT, SHOW, DESCRIBE, EXPLAIN query');
  290. $this->result = $result;
  291. $this->current_row = 0;
  292. $this->total_rows = $this->sqlite_row_count();
  293. $this->fetch_type = ($object === TRUE) ? PDO::FETCH_OBJ : PDO::FETCH_ASSOC;
  294. }
  295. elseif (preg_match('/^DELETE|INSERT|UPDATE/i', $sql))
  296. {
  297. //Log::add('debug','Its an DELETE, INSERT, REPLACE, or UPDATE query');
  298. $this->insert_id = $link->lastInsertId();
  299. }
  300. }
  301. else
  302. {
  303. // SQL error
  304. throw new Kohana_Database_Exception('database.error', $link->errorInfo().' - '.$sql);
  305. }
  306. // Set result type
  307. $this->result($object);
  308. }
  309. private function sqlite_row_count()
  310. {
  311. // workaround for PDO not supporting RowCount with SQLite - we manually count
  312. //TODO : can this be fixed?
  313. $count = 0;
  314. while ($this->result->fetch()) {
  315. $count++;
  316. }
  317. // now the really dumb part: need to re-execute the query
  318. $this->result->execute();
  319. return $count;
  320. }
  321. /*
  322. * Destructor: __destruct
  323. * Magic __destruct function, frees the result.
  324. */
  325. public function __destruct()
  326. {
  327. if (is_object($this->result))
  328. {
  329. $this->result->closeCursor();
  330. $this->result = NULL;
  331. }
  332. }
  333. public function result($object = TRUE, $type = PDO::FETCH_BOTH)
  334. {
  335. $rows = array();
  336. $this->fetch_type = (bool) $object ? PDO::FETCH_OBJ : PDO::FETCH_BOTH;
  337. if ($this->fetch_type == PDO::FETCH_OBJ)
  338. {
  339. $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
  340. }
  341. else
  342. {
  343. $this->return_type = $type;
  344. }
  345. return $this;
  346. }
  347. public function result_array($object = NULL, $type = PDO::FETCH_ASSOC)
  348. {
  349. $rows = array();
  350. if (is_string($object))
  351. {
  352. $fetch = $object;
  353. }
  354. elseif (is_bool($object))
  355. {
  356. if ($object === TRUE)
  357. {
  358. $fetch = PDO::FETCH_OBJ;
  359. // NOTE - The class set by $type must be defined before fetching the result,
  360. // autoloading is disabled to save a lot of stupid overhead.
  361. $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
  362. }
  363. else
  364. {
  365. $fetch = PDO::FETCH_OBJ;
  366. }
  367. }
  368. else
  369. {
  370. // Use the default config values
  371. $fetch = $this->fetch_type;
  372. if ($fetch == PDO::FETCH_OBJ)
  373. {
  374. $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
  375. }
  376. }
  377. try
  378. {
  379. while ($row = $this->result->fetch($fetch))
  380. {
  381. $rows[] = $row;
  382. }
  383. }
  384. catch(PDOException $e)
  385. {
  386. throw new Kohana_Database_Exception('database.error', $e->getMessage());
  387. return FALSE;
  388. }
  389. return $rows;
  390. }
  391. public function insert_id()
  392. {
  393. return $this->insert_id;
  394. }
  395. public function list_fields()
  396. {
  397. //~ This function only work correctly after you execute a query,
  398. //~ AND BEFORE you fetch the query result!!
  399. //~ You should really use Database_PdoSqlite::list_fields instead of PdoSqlite_Result::list_fields()
  400. Log::add('debug','If Sqlite_Result::list_fields() do NOT work as what you expect,read the method\'s comment plz');
  401. $field_names = array();
  402. for ($i = 0; $i<$this->result->columnCount(); $i++)
  403. {
  404. $colInfo = $this->result->getColumnMeta($i);
  405. $field_names[] = $colInfo['name'];
  406. }
  407. return $field_names;
  408. }
  409. // End Interface
  410. // Interface: Countable
  411. /*
  412. * Method: count
  413. * Counts the number of rows in the result set.
  414. *
  415. * Returns:
  416. * The number of rows in the result set
  417. *
  418. */
  419. public function count()
  420. {
  421. //~ Now only work after calling result() or result_array();
  422. Log::add('debug', 'Now only work after calling result() or result_array()');
  423. return $this->total_rows;
  424. }
  425. public function num_rows()
  426. {
  427. Log::add('error', 'You should really be using "count($result)" instead of "$result->num_rows()". Fix your code!');
  428. return $this->total_rows;
  429. }
  430. // End Interface
  431. // Interface: ArrayAccess
  432. /*
  433. * Method: offsetExists
  434. * Determines if the requested offset of the result set exists.
  435. *
  436. * Parameters:
  437. * offset - offset id
  438. *
  439. * Returns:
  440. * TRUE if the offset exists, FALSE otherwise
  441. *
  442. */
  443. public function offsetExists($offset)
  444. {
  445. if ($this->total_rows > 0)
  446. {
  447. $min = 0;
  448. $max = $this->total_rows - 1;
  449. return ($offset < $min OR $offset > $max) ? FALSE : TRUE;
  450. }
  451. return FALSE;
  452. }
  453. /*
  454. * Method: offsetGet
  455. * Retreives the requested query result offset.
  456. *
  457. * Parameters:
  458. * offset - offset id
  459. *
  460. * Returns:
  461. * The query row
  462. *
  463. */
  464. public function offsetGet($offset)
  465. {
  466. $row = array();
  467. try
  468. {
  469. $row = $this->result->fetch($this->fetch_type, PDO::FETCH_ORI_ABS, $offset);
  470. }
  471. catch(PDOException $e)
  472. {
  473. throw new Kohana_Database_Exception('database.error', $e->getMessage());
  474. return FALSE;
  475. }
  476. return $row;
  477. }
  478. /*
  479. * Method: offsetSet
  480. * Sets the offset with the provided value. Since you can't modify query result sets, this function just throws an exception.
  481. *
  482. * Parameters:
  483. * offset - offset id
  484. * value - value to set
  485. *
  486. * Returns:
  487. * <Kohana_Database_Exception> object
  488. *
  489. */
  490. public function offsetSet($offset, $value)
  491. {
  492. throw new Kohana_Database_Exception('database.result_read_only');
  493. }
  494. /*
  495. * Method: offsetUnset
  496. * Unsets the offset. Since you can't modify query result sets, this function just throws an exception.
  497. *
  498. * Parameters:
  499. * offset - offset id
  500. *
  501. * Returns:
  502. * <Kohana_Database_Exception> object
  503. *
  504. */
  505. public function offsetUnset($offset)
  506. {
  507. throw new Kohana_Database_Exception('database.result_read_only');
  508. }
  509. // End Interface
  510. // Interface: Iterator
  511. /*
  512. * Method: current
  513. * Retreives the current result set row.
  514. *
  515. * Returns:
  516. * The current result row (type based on <PdoSqlite_result.result>)
  517. *
  518. */
  519. public function current()
  520. {
  521. return $this->offsetGet($this->current_row);
  522. }
  523. /*
  524. * Method: key
  525. * Retreives the current row id.
  526. *
  527. * Returns:
  528. * The current result row id
  529. *
  530. */
  531. public function key()
  532. {
  533. return $this->current_row;
  534. }
  535. /*
  536. * Method: next
  537. * Moves the result pointer ahead one.
  538. *
  539. * Returns:
  540. * The next row id
  541. *
  542. */
  543. public function next()
  544. {
  545. return ++$this->current_row;
  546. }
  547. /*
  548. * Method: next
  549. * Moves the result pointer back one.
  550. *
  551. * Returns:
  552. * The previous row id
  553. *
  554. */
  555. public function prev()
  556. {
  557. return --$this->current_row;
  558. }
  559. /*
  560. * Method: rewind
  561. * Moves the result pointer to the beginning of the result set.
  562. *
  563. * Returns:
  564. * 0
  565. *
  566. */
  567. public function rewind()
  568. {
  569. //~ To request a scrollable cursor for your PDOStatement object,
  570. //~ you must set the PDO::ATTR_CURSOR attribute to PDO::CURSOR_SCROLL
  571. //~ when you prepare the SQL statement with PDO->prepare().
  572. Log::add('error','this method do not work now,please read the comment of that.');
  573. //return $this->current_row = 0;
  574. }
  575. /*
  576. * Method: valid
  577. * Determines if the current result pointer is valid.
  578. *
  579. * Returns:
  580. * TRUE if the pointer is valid, FALSE otherwise
  581. *
  582. */
  583. public function valid()
  584. {
  585. return $this->offsetExists($this->current_row);
  586. }
  587. // End Interface
  588. } // End PdoSqlite_Result Class