209f115e76740a39581c80405cc07ad61b61650a
[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 define('CONFIG_DIR', dirname(__FILE__) . '/../../');
31
32 function config($key) {
33     global $_config;
34     if (!$_config)
35         $_config = json_decode(file_get_contents(CONFIG_DIR . 'config.json'), true);
36     return $_config[$key];
37 }
38
39 function generate_data_file($filename, $content) {
40     if (!assert(ctype_alnum(str_replace(array('-', '_', '.'), '', $filename))))
41         return FALSE;
42     return file_put_contents(CONFIG_DIR . config('dataDirectory') . '/' . $filename, $content);
43 }
44
45 if (config('debug')) {
46     error_reporting(E_ALL | E_STRICT);
47     ini_set('display_errors', 'On');
48 } else
49     error_reporting(E_ERROR);
50
51 date_default_timezone_set('UTC');
52
53 class Database
54 {
55     private $connection = false;
56
57     function __destruct() {
58         if ($this->connection)
59             pg_close($this->connection);
60         $this->connection = false;
61     }
62
63     static function is_true($value) {
64         return $value == 't';
65     }
66
67     static function to_database_boolean($value) {
68         return $value ? 't' : 'f';
69     }
70
71     static function to_js_time($time_str) {
72         $timestamp_in_s = strtotime($time_str);
73         $dot_index = strrpos($time_str, '.');
74         if ($dot_index !== FALSE)
75             $timestamp_in_s += floatval(substr($time_str, $dot_index));
76         return intval($timestamp_in_s * 1000);
77     }
78
79     function connect() {
80         $databaseConfig = config('database');
81         $this->connection = pg_connect('host=' . $databaseConfig['host'] . ' port=' . $databaseConfig['port']
82             . ' dbname=' . $databaseConfig['name'] . ' user=' . $databaseConfig['username'] . ' password=' . $databaseConfig['password']);
83         return $this->connection ? true : false;
84     }
85
86     private function prefixed_column_names($columns, $prefix = NULL) {
87         if (!$prefix || !$columns)
88             return join(', ', $columns);
89         return $prefix . '_' . join(', ' . $prefix . '_', $columns);
90     }
91
92     private function prefixed_name($column, $prefix = NULL) {
93         return $prefix ? $prefix . '_' . $column : $column;
94     }
95
96     private function prepare_params($params, &$placeholders, &$values) {
97         $column_names = array_keys($params);
98
99         $i = count($values) + 1;
100         foreach ($column_names as $name) {
101             assert(ctype_alnum_underscore($name));
102             array_push($placeholders, '$' . $i);
103             array_push($values, $params[$name]);
104             $i++;
105         }
106
107         return $column_names;
108     }
109
110     function insert_row($table, $prefix, $params, $returning = 'id') {
111         $placeholders = array();
112         $values = array();
113         $column_names = $this->prepare_params($params, $placeholders, $values);
114
115         assert(!$prefix || ctype_alnum_underscore($prefix));
116         $column_names = $this->prefixed_column_names($column_names, $prefix);
117         $placeholders = join(', ', $placeholders);
118
119         $value_query = $column_names ? "($column_names) VALUES ($placeholders)" : ' VALUES (default)';
120         if ($returning) {
121             $returning_column_name = $this->prefixed_name($returning, $prefix);
122             $rows = $this->query_and_fetch_all("INSERT INTO $table $value_query RETURNING $returning_column_name", $values);
123             return $rows ? $rows[0][$returning_column_name] : NULL;
124         }
125
126         return $this->query_and_get_affected_rows("INSERT INTO $table $value_query", $values) == 1;
127     }
128
129     function select_or_insert_row($table, $prefix, $select_params, $insert_params = NULL, $returning = 'id') {
130         return $this->_select_update_or_insert_row($table, $prefix, $select_params, $insert_params, $returning, FALSE, TRUE);
131     }
132
133     function update_or_insert_row($table, $prefix, $select_params, $insert_params = NULL, $returning = 'id') {
134         return $this->_select_update_or_insert_row($table, $prefix, $select_params, $insert_params, $returning, TRUE, TRUE);
135     }
136
137     function update_row($table, $prefix, $select_params, $update_params, $returning = 'id') {
138         return $this->_select_update_or_insert_row($table, $prefix, $select_params, $update_params, $returning, TRUE, FALSE);
139     }
140
141     private function _select_update_or_insert_row($table, $prefix, $select_params, $insert_params, $returning, $should_update, $should_insert) {
142         $values = array();
143
144         $select_placeholders = array();
145         $select_column_names = $this->prepare_params($select_params, $select_placeholders, $values);
146         $select_values = array_slice($values, 0);
147
148         if ($insert_params === NULL)
149             $insert_params = $select_params;
150         $insert_placeholders = array();
151         $insert_column_names = $this->prepare_params($insert_params, $insert_placeholders, $values);
152
153         assert(!!$returning);
154         assert(!$prefix || ctype_alnum_underscore($prefix));
155         $returning_column_name = $returning == '*' ? '*' : $this->prefixed_name($returning, $prefix);
156         $select_column_names = $this->prefixed_column_names($select_column_names, $prefix);
157         $select_placeholders = join(', ', $select_placeholders);
158         $query = "SELECT $returning_column_name FROM $table WHERE ($select_column_names) = ($select_placeholders)";
159
160         $insert_column_names = $this->prefixed_column_names($insert_column_names, $prefix);
161         $insert_placeholders = join(', ', $insert_placeholders);
162
163         // http://stackoverflow.com/questions/1109061/insert-on-duplicate-update-in-postgresql
164         $rows = NULL;
165         if ($should_update) {
166             $rows = $this->query_and_fetch_all("UPDATE $table SET ($insert_column_names) = ($insert_placeholders)
167                 WHERE ($select_column_names) = ($select_placeholders) RETURNING $returning_column_name", $values);
168         }
169         if (!$rows && $should_insert) {
170             $rows = $this->query_and_fetch_all("INSERT INTO $table ($insert_column_names) SELECT $insert_placeholders
171                 WHERE NOT EXISTS ($query) RETURNING $returning_column_name", $values);            
172         }
173         if (!$should_update && !$rows)
174             $rows = $this->query_and_fetch_all($query, $select_values);
175
176         return $rows ? ($returning == '*' ? $rows[0] : $rows[0][$returning_column_name]) : NULL;
177     }
178
179     function select_first_row($table, $prefix, $params, $order_by = NULL) {
180         return $this->select_first_or_last_row($table, $prefix, $params, $order_by, FALSE);
181     }
182
183     function select_last_row($table, $prefix, $params, $order_by = NULL) {
184         return $this->select_first_or_last_row($table, $prefix, $params, $order_by, TRUE);
185     }
186
187     private function select_first_or_last_row($table, $prefix, $params, $order_by, $descending_order) {
188         $rows = $this->select_rows($table, $prefix, $params, $order_by, $descending_order, 0, 1);
189         return $rows ? $rows[0] : NULL;
190     }
191
192     function select_rows($table, $prefix, $params,
193         $order_by = NULL, $descending_order = FALSE, $offset = NULL, $limit = NULL) {
194
195         $placeholders = array();
196         $values = array();
197         $column_names = $this->prefixed_column_names($this->prepare_params($params, $placeholders, $values), $prefix);
198         $placeholders = join(', ', $placeholders);
199         if (!$column_names && !$placeholders)
200             $column_names = $placeholders = '1';
201         $query = "SELECT * FROM $table WHERE ($column_names) = ($placeholders)";
202         if ($order_by) {
203             assert(ctype_alnum_underscore($order_by));
204             $query .= ' ORDER BY ' . $this->prefixed_name($order_by, $prefix);
205             if ($descending_order)
206                 $query .= ' DESC';
207         }
208         if ($offset !== NULL)
209             $query .= ' OFFSET ' . intval($offset);
210         if ($limit !== NULL)
211             $query .= ' LIMIT ' . intval($limit);
212
213         return $this->query_and_fetch_all($query, $values);
214     }
215
216     function query_and_get_affected_rows($query, $params = array()) {
217         if (!$this->connection)
218             return FALSE;
219         $result = pg_query_params($this->connection, $query, $params);
220         if (!$result)
221             return FALSE;
222         return pg_affected_rows($result);
223     }
224
225     function query_and_fetch_all($query, $params = array()) {
226         if (!$this->connection)
227             return NULL;
228         $result = pg_query_params($this->connection, $query, $params);
229         if (!$result)
230             return NULL;
231         if (pg_num_rows($result) == 0)
232             return array();
233         return pg_fetch_all($result);
234     }
235
236     function query($query, $params = array()) {
237         if (!$this->connection)
238             return FALSE;
239         return pg_query_params($this->connection, $query, $params);
240     }
241
242     function fetch_next_row($result) {
243         return pg_fetch_assoc($result);
244     }
245
246     function fetch_table($table_name, $column_to_be_ordered_by = null) {
247         if (!$this->connection || !ctype_alnum_underscore($table_name) || ($column_to_be_ordered_by && !ctype_alnum_underscore($column_to_be_ordered_by)))
248             return false;
249         $clauses = '';
250         if ($column_to_be_ordered_by)
251             $clauses .= 'ORDER BY ' . $column_to_be_ordered_by;
252         return $this->query_and_fetch_all("SELECT * FROM $table_name $clauses");
253     }
254
255     function begin_transaction() {
256         return $this->connection and pg_query($this->connection, "BEGIN");
257     }
258
259     function commit_transaction() {
260         return $this->connection and pg_query($this->connection, 'COMMIT');
261     }
262
263     function rollback_transaction() {
264         return $this->connection and pg_query($this->connection, 'ROLLBACK');
265     }
266
267 }
268
269 ?>