SqlQuery.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <?php
  2. namespace KarmaFW\Database\Sql;
  3. use \KarmaFW\App;
  4. use \KarmaFW\Database\Sql\SqlResultSetError;
  5. class SqlQuery
  6. {
  7. protected $db;
  8. protected $duration = null;
  9. protected $error = null;
  10. protected $recordset = null;
  11. protected $status = 'ready';
  12. protected $query = null;
  13. protected $results_rows_count = null;
  14. protected $affected_rows_count = null;
  15. public function __construct($db, $query=null)
  16. {
  17. $this->db = $db;
  18. $this->query = $query;
  19. }
  20. public function __toString()
  21. {
  22. return $this->getQuery();
  23. }
  24. public function __debugInfo() {
  25. return [
  26. //'db:protected' => get_class($this->db) ." Object",
  27. 'db:protected' => $this->db,
  28. 'status:protected' => $this->status,
  29. 'error:protected' => $this->error,
  30. 'query:protected' => $this->query,
  31. 'duration:protected' => $this->duration,
  32. //'recordset:protected' => get_class($this->recordset) ." Object",
  33. 'recordset:protected' => $this->recordset,
  34. 'results_rows_count:protected' => $this->results_rows_count,
  35. 'affected_rows_count:protected' => $this->affected_rows_count,
  36. ];
  37. }
  38. /*
  39. public function fetchColumn($column_name)
  40. {
  41. if ($this->status == 'ready') {
  42. $this->execute();
  43. }
  44. return $this->recordset->fetchColumn($column_name);
  45. }
  46. public function fetchOne()
  47. {
  48. if ($this->status == 'ready') {
  49. $this->execute();
  50. }
  51. return $this->recordset->fetchOne();
  52. }
  53. public function fetchAll()
  54. {
  55. if ($this->status == 'ready') {
  56. $this->execute();
  57. }
  58. return $this->recordset->fetchAll();
  59. }
  60. */
  61. public function getQuery()
  62. {
  63. return $this->query;
  64. }
  65. public function getStatus()
  66. {
  67. return $this->status;
  68. }
  69. public function execute($query=null, $params=[], $nb_tries=1)
  70. {
  71. if (! $this->db->isConnected()) {
  72. $this->db->connect();
  73. }
  74. if (empty($query)) {
  75. $query = $this->query;
  76. }
  77. if (! empty($params)) {
  78. $parts = explode('?', $query);
  79. $query = array_shift($parts);
  80. $i = 1;
  81. foreach ($params as $param) {
  82. if (empty($parts)) {
  83. throw new \Exception('SqlQuery::execute() => params error (1) [QUERY: ' . preg_replace('/\s+/', ' ', $query) . ' ]');
  84. }
  85. $param = $this->db->escape($param);
  86. $query .= $param;
  87. $query .= array_shift($parts);
  88. $i++;
  89. }
  90. if (! empty($parts)) {
  91. throw new \Exception('SqlQuery::execute() => params error (2) [QUERY: ' . preg_replace('/\s+/', ' ', $query) . ' ]');
  92. }
  93. }
  94. $this->query = preg_replace('/\s+/', ' ', $query);
  95. $this->status = 'running';
  96. $ts_start = microtime(true);
  97. if (! empty($_GET['debug_sql'])) {
  98. echo $query . "<hr />";
  99. }
  100. $_query = $query;
  101. if (! empty($_GET['dry_sql'])) {
  102. $_query = "select 1 from ( select 1 ) tmp where 0";
  103. }
  104. $mem_start = memory_get_usage();
  105. $rs = $this->db->getDriver()->execute($_query);
  106. $mem_end = memory_get_usage();
  107. $memory_used = $mem_end - $mem_start;
  108. //pre($rs);
  109. $ts_end = microtime(true);
  110. $this->duration = $ts_end - $ts_start;
  111. $this->recordset = $rs;
  112. $this->db->setLastQuery($this);
  113. // debugbar
  114. $debugbar = App::getData('debugbar');
  115. if ($debugbar) {
  116. //$debugbar['sql']->addMessage( preg_replace('/\s+/', ' ', $query) );
  117. if (isset($debugbar['sql_queries'])) {
  118. $error_code = 0;
  119. $error_msg = null;
  120. $is_success = true;
  121. if ($rs instanceOf SqlResultSetError) {
  122. $is_success = false;
  123. $error_code = $rs->getErrorCode();
  124. $error_msg = $rs->getErrorMessage();
  125. }
  126. $debugbar['sql_queries']->addQuery([
  127. 'sql' => preg_replace('/\s+/', ' ', $query),
  128. 'duration' => $this->duration,
  129. 'duration_str' => formatDuration($this->duration),
  130. 'row_count' => $rs->getRowsCount(),
  131. 'params' => $params,
  132. 'memory' => $memory_used,
  133. 'memory_str' => formatSize($memory_used),
  134. 'end_memory' => $mem_end,
  135. 'end_memory_str' => formatSize($mem_end),
  136. 'is_success' => $is_success,
  137. 'error_code' => $error_code,
  138. 'error_message' => $error_msg,
  139. ]);
  140. }
  141. }
  142. if ($rs instanceOf SqlResultSetError) {
  143. // query error
  144. $error_code = $rs->getErrorCode();
  145. $error_msg = $rs->getErrorMessage();
  146. $this->error = $error_msg;
  147. $this->status = 'error';
  148. if ($error_code == 2006) {
  149. // MySQL server has gone away
  150. if ($nb_tries > 1) {
  151. sleep(1);
  152. return $this->execute($query, $params, $nb_tries-1);
  153. }
  154. }
  155. if ($this->db->throwOnSqlError) {
  156. throw new \Exception('SqlQuery::execute() => DB error [' . $error_code . '] : ' . $error_msg . PHP_EOL . '[QUERY: ' . preg_replace('/\s+/', ' ', $query) . ' ]');
  157. }
  158. //return null;
  159. } else {
  160. $this->status = 'success';
  161. }
  162. $this->results_rows_count = $rs->getRowsCount();
  163. $this->affected_rows_count = $this->db->getDriver()->getAffectedRowsCount();
  164. //return $this;
  165. return $rs;
  166. }
  167. public function executeSelect($query, $params=[])
  168. {
  169. // Alias of executeSelectAll
  170. return $this->executeSelectAll($query, $params);
  171. }
  172. public function executeSelectOne($query, $params=[])
  173. {
  174. return $this->execute($query, $params)->fetchOne();
  175. }
  176. public function executeSelectValue($query, $column_name, $params=[])
  177. {
  178. return $this->execute($query, $params)->fetchColumn($column_name);
  179. }
  180. public function executeSelectAll($query, $params=[])
  181. {
  182. return $this->execute($query, $params)->fetchAll();
  183. }
  184. public function executeSelectAllWithFoundRows($query, $params=[])
  185. {
  186. if (strpos($query, 'SQL_CALC_FOUND_ROWS') === false) {
  187. $query = str_replace("select ", "select SQL_CALC_FOUND_ROWS ", $query);
  188. }
  189. $rs = $this->execute($query, $params);
  190. $data = $rs->fetchAll();
  191. $found_rows = $rs->getfoundRowsCount();
  192. return ['found_rows' => $found_rows, 'data' => $data];
  193. }
  194. public function executeSelectAllPagination($query, $nb_per_page=10, $page_idx=1, $params=[])
  195. {
  196. if (true) {
  197. $page_idx = max(1, intval($page_idx));
  198. $nb_per_page = max(1, intval($nb_per_page));
  199. $offset = ($page_idx - 1) * $nb_per_page;
  200. $limit = $offset . ', ' . $nb_per_page;
  201. $query .= " limit " . $limit;
  202. }
  203. $result = $this->executeSelectAllWithFoundRows($query, $params);
  204. $found_rows = $result['found_rows'];
  205. $data = $result['data'];
  206. $pagination = [
  207. 'page' => $page_idx,
  208. 'limit' => $nb_per_page,
  209. 'offset' => $offset,
  210. 'page_rows' => count($data),
  211. 'total_rows' => $found_rows,
  212. 'nb_pages' => empty($nb_per_page) ? null : ceil($found_rows / $nb_per_page),
  213. ];
  214. return [
  215. 'pagination' => $pagination,
  216. 'data' => $data,
  217. ];
  218. }
  219. public function executeInsert($query, $params=[])
  220. {
  221. $this->execute($query, $params);
  222. return $this->db->getInsertId();
  223. }
  224. public function executeInsertAll($query, $params=[])
  225. {
  226. return $this->execute($query, $params);
  227. }
  228. public function executeUpdate($query, $params=[])
  229. {
  230. $this->execute($query, $params);
  231. return $this->affected_rows_count;
  232. }
  233. public function executeDelete($query, $params=[])
  234. {
  235. $this->execute($query, $params);
  236. return $this->affected_rows_count;
  237. }
  238. /* ### */
  239. public function tableSelect($table_name, $where=[], $options=[])
  240. {
  241. // Alias of tableSelectAll
  242. return $this->tableSelectAll($table_name, $where, $options);
  243. }
  244. public function tableSelectAll($table_name, $where=[], $options=[])
  245. {
  246. $table = new SqlTable($table_name, $this->db);
  247. $query = $table->buildQuery($where, $options);
  248. return $this->executeSelectAll($query);
  249. }
  250. public function tableSelectOne($table_name, $where=[], $options=[])
  251. {
  252. if (empty($options)) {
  253. $options = [];
  254. }
  255. $options['limit'] = 1;
  256. $table = new SqlTable($table_name, $this->db);
  257. $query = $table->buildQuery($where, $options);
  258. return $this->executeSelectOne($query);
  259. }
  260. public function tableInsert($table_name, $values=[], $options=[])
  261. {
  262. $this->tableInsertAll($table_name, [$values], $options);
  263. return $this->db->getInsertId();
  264. }
  265. public function tableInsertAll($table_name, $inserts=[], $options=[])
  266. {
  267. $table = new SqlTable($table_name, $this->db);
  268. return $table->insertAll($inserts, $options);
  269. }
  270. public function tableUpdate($table_name, $updates=[], $where=[], $options=[])
  271. {
  272. $table = new SqlTable($table_name, $this->db);
  273. return $table->update($updates, $where, $options);
  274. }
  275. public function tableDelete($table_name, $where=[], $options=[])
  276. {
  277. $table = new SqlTable($table_name, $this->db);
  278. return $table->delete($where, $options);
  279. }
  280. }