Cache-control should be set only on api/runs
[WebKit-https.git] / Websites / perf.webkit.org / public / include / db.php
1 <?php
2
3 function ends_with($str, $key) {
4     return strrpos($str, $key) == strlen($str) - strlen($key);
5 }
6
7 function ctype_alnum_underscore($str) {
8     return ctype_alnum(str_replace('_', '', $str));
9 }
10
11 function &array_ensure_item_has_array(&$array, $key) {
12     if (!array_key_exists($key, $array))
13         $array[$key] = array();
14     return $array[$key];
15 }
16
17 function array_get($array, $key, $default = NULL) {
18     if (!array_key_exists($key, $array))
19         return $default;
20     return $array[$key];
21 }
22
23 function array_set_default(&$array, $key, $default) {
24     if (!array_key_exists($key, $array))
25         $array[$key] = $default;
26 }
27
28 $_config = NULL;
29
30 function config($key) {
31     global $_config;
32     if (!$_config)
33         $_config = json_decode(file_get_contents(dirname(__FILE__) . '/../../config.json'), true);
34     return $_config[$key];
35 }
36
37 if (config('debug')) {
38     error_reporting(E_ALL | E_STRICT);
39     ini_set('display_errors', 'On');
40 } else
41     error_reporting(E_ERROR);
42
43 date_default_timezone_set('UTC');
44
45 class Database
46 {
47     private $connection = false;
48
49     function __destruct() {
50         if ($this->connection)
51             pg_close($this->connection);
52         $this->connection = false;
53     }
54
55     function is_true($value) {
56         return $value == 't';
57     }
58
59     function connect() {
60         $databaseConfig = config('database');
61         $this->connection = pg_connect('host=' . $databaseConfig['host'] . ' port=' . $databaseConfig['port']
62             . ' dbname=' . $databaseConfig['name'] . ' user=' . $databaseConfig['username'] . ' password=' . $databaseConfig['password']);
63         return $this->connection ? true : false;
64     }
65
66     private function prefixed_column_names($columns, $prefix = NULL) {
67         if (!$prefix)
68             return join(', ', $columns);
69         return $prefix . '_' . join(', ' . $prefix . '_', $columns);
70     }
71
72     private function prefixed_name($column, $prefix = NULL) {
73         return $prefix ? $prefix . '_' . $column : $column;
74     }
75
76     private function prepare_params($params, &$placeholders, &$values) {
77         $column_names = array_keys($params);
78
79         $i = count($values) + 1;
80         foreach ($column_names as $name) {
81             assert(ctype_alnum_underscore($name));
82             array_push($placeholders, '$' . $i);
83             array_push($values, $params[$name]);
84             $i++;
85         }
86
87         return $column_names;
88     }
89
90     function insert_row($table, $prefix, $params, $returning = 'id') {
91         $placeholders = array();
92         $values = array();
93         $column_names = $this->prepare_params($params, $placeholders, $values);
94
95         assert(!$prefix || ctype_alnum_underscore($prefix));
96         $column_names = $this->prefixed_column_names($column_names, $prefix);
97         $placeholders = join(', ', $placeholders);
98
99         if ($returning) {
100             $returning_column_name = $this->prefixed_name($returning, $prefix);
101             $rows = $this->query_and_fetch_all("INSERT INTO $table ($column_names) VALUES ($placeholders) RETURNING $returning_column_name", $values);
102             return $rows ? $rows[0][$returning_column_name] : NULL;
103         }
104
105         return $this->query_and_get_affected_rows("INSERT INTO $table ($column_names) VALUES ($placeholders)", $values) == 1;
106     }
107
108     function select_or_insert_row($table, $prefix, $select_params, $insert_params = NULL, $returning = 'id') {
109         return $this->_select_update_or_insert_row($table, $prefix, $select_params, $insert_params, $returning, FALSE);
110     }
111
112     function update_or_insert_row($table, $prefix, $select_params, $insert_params = NULL, $returning = 'id') {
113         return $this->_select_update_or_insert_row($table, $prefix, $select_params, $insert_params, $returning, TRUE);
114     }
115
116     private function _select_update_or_insert_row($table, $prefix, $select_params, $insert_params, $returning, $should_update) {
117         $values = array();
118
119         $select_placeholders = array();
120         $select_column_names = $this->prepare_params($select_params, $select_placeholders, $values);
121         $select_values = array_slice($values, 0);
122
123         if ($insert_params === NULL)
124             $insert_params = $select_params;
125         $insert_placeholders = array();
126         $insert_column_names = $this->prepare_params($insert_params, $insert_placeholders, $values);
127
128         assert(!!$returning);
129         assert(!$prefix || ctype_alnum_underscore($prefix));
130         $returning_column_name = $returning == '*' ? '*' : $this->prefixed_name($returning, $prefix);
131         $select_column_names = $this->prefixed_column_names($select_column_names, $prefix);
132         $select_placeholders = join(', ', $select_placeholders);
133         $query = "SELECT $returning_column_name FROM $table WHERE ($select_column_names) = ($select_placeholders)";
134
135         $insert_column_names = $this->prefixed_column_names($insert_column_names, $prefix);
136         $insert_placeholders = join(', ', $insert_placeholders);
137
138         // http://stackoverflow.com/questions/1109061/insert-on-duplicate-update-in-postgresql
139         $rows = NULL;
140         if ($should_update) {
141             $rows = $this->query_and_fetch_all("UPDATE $table SET ($insert_column_names) = ($insert_placeholders)
142                 WHERE ($select_column_names) = ($select_placeholders) RETURNING $returning_column_name", $values);
143         }
144         if (!$rows) {
145             $rows = $this->query_and_fetch_all("INSERT INTO $table ($insert_column_names) SELECT $insert_placeholders
146                 WHERE NOT EXISTS ($query) RETURNING $returning_column_name", $values);            
147         }
148         if (!$should_update && !$rows)
149             $rows = $this->query_and_fetch_all($query, $select_values);
150
151         return $rows ? ($returning == '*' ? $rows[0] : $rows[0][$returning_column_name]) : NULL;
152     }
153
154     function select_first_row($table, $prefix, $params, $order_by = NULL) {
155         return $this->select_first_or_last_row($table, $prefix, $params, $order_by, FALSE);
156     }
157
158     function select_last_row($table, $prefix, $params, $order_by = NULL) {
159         return $this->select_first_or_last_row($table, $prefix, $params, $order_by, TRUE);
160     }
161
162     private function select_first_or_last_row($table, $prefix, $params, $order_by, $descending_order) {
163         $rows = $this->select_rows($table, $prefix, $params, $order_by, $descending_order, 0, 1);
164         return $rows ? $rows[0] : NULL;
165     }
166
167     function select_rows($table, $prefix, $params,
168         $order_by = NULL, $descending_order = FALSE, $offset = NULL, $limit = NULL) {
169
170         $placeholders = array();
171         $values = array();
172         $column_names = $this->prefixed_column_names($this->prepare_params($params, $placeholders, $values), $prefix);
173         $placeholders = join(', ', $placeholders);
174         $query = "SELECT * FROM $table WHERE ($column_names) = ($placeholders)";
175         if ($order_by) {
176             assert(ctype_alnum_underscore($order_by));
177             $query .= ' ORDER BY ' . $this->prefixed_name($order_by, $prefix);
178             if ($descending_order)
179                 $query .= ' DESC';
180         }
181         if ($offset !== NULL)
182             $query .= ' OFFSET ' . intval($offset);
183         if ($limit !== NULL)
184             $query .= ' LIMIT ' . intval($limit);
185
186         return $this->query_and_fetch_all($query, $values);
187     }
188
189     function query_and_get_affected_rows($query, $params = array()) {
190         if (!$this->connection)
191             return FALSE;
192         $result = pg_query_params($this->connection, $query, $params);
193         if (!$result)
194             return FALSE;
195         return pg_affected_rows($result);
196     }
197
198     function query_and_fetch_all($query, $params = array()) {
199         if (!$this->connection)
200             return NULL;
201         $result = pg_query_params($this->connection, $query, $params);
202         if (!$result)
203             return NULL;
204         if (pg_num_rows($result) == 0)
205             return array();
206         return pg_fetch_all($result);
207     }
208
209     function query($query, $params = array()) {
210         if (!$this->connection)
211             return FALSE;
212         return pg_query_params($this->connection, $query, $params);
213     }
214
215     function fetch_next_row($result) {
216         return pg_fetch_assoc($result);
217     }
218
219     function fetch_table($table_name, $column_to_be_ordered_by = null) {
220         if (!$this->connection || !ctype_alnum_underscore($table_name) || ($column_to_be_ordered_by && !ctype_alnum_underscore($column_to_be_ordered_by)))
221             return false;
222         $clauses = '';
223         if ($column_to_be_ordered_by)
224             $clauses .= 'ORDER BY ' . $column_to_be_ordered_by;
225         return $this->query_and_fetch_all("SELECT * FROM $table_name $clauses");
226     }
227
228     function begin_transaction() {
229         return $this->connection and pg_query($this->connection, "BEGIN");
230     }
231
232     function commit_transaction() {
233         return $this->connection and pg_query($this->connection, 'COMMIT');
234     }
235
236     function rollback_transaction() {
237         return $this->connection and pg_query($this->connection, 'ROLLBACK');
238     }
239
240 }
241
242 ?>