3 function ends_with($str, $key) {
4 return strrpos($str, $key) == strlen($str) - strlen($key);
7 function ctype_alnum_underscore($str) {
8 return ctype_alnum(str_replace('_', '', $str));
11 function &array_ensure_item_has_array(&$array, $key) {
12 if (!array_key_exists($key, $array))
13 $array[$key] = array();
17 function array_get($array, $key, $default = NULL) {
18 if (!array_key_exists($key, $array))
23 function array_set_default(&$array, $key, $default) {
24 if (!array_key_exists($key, $array))
25 $array[$key] = $default;
30 define('CONFIG_DIR', dirname(__FILE__) . '/../../');
32 function config($key, $default = NULL) {
35 $_config = json_decode(file_get_contents(CONFIG_DIR . 'config.json'), true);
36 return array_get($_config, $key, $default);
39 function config_path($key, $path) {
40 return CONFIG_DIR . config($key) . '/' . $path;
43 function generate_data_file($filename, $content) {
44 if (!assert(ctype_alnum(str_replace(array('-', '_', '.'), '', $filename))))
46 return file_put_contents(config_path('dataDirectory', $filename), $content);
49 if (config('debug')) {
50 error_reporting(E_ALL | E_STRICT);
51 ini_set('display_errors', 'On');
53 error_reporting(E_ERROR);
55 date_default_timezone_set('UTC');
59 private $connection = false;
61 function __destruct() {
62 if ($this->connection)
63 pg_close($this->connection);
64 $this->connection = false;
67 static function is_true($value) {
71 static function to_database_boolean($value) {
72 return $value ? 't' : 'f';
75 static function to_js_time($time_str) {
76 $timestamp_in_s = strtotime($time_str);
77 $dot_index = strrpos($time_str, '.');
78 if ($dot_index !== FALSE)
79 $timestamp_in_s += floatval(substr($time_str, $dot_index));
80 return intval($timestamp_in_s * 1000);
84 $databaseConfig = config('database');
85 $this->connection = pg_connect('host=' . $databaseConfig['host'] . ' port=' . $databaseConfig['port']
86 . ' dbname=' . $databaseConfig['name'] . ' user=' . $databaseConfig['username'] . ' password=' . $databaseConfig['password']);
87 return $this->connection ? true : false;
90 private function prefixed_column_names($columns, $prefix = NULL) {
91 if (!$prefix || !$columns)
92 return join(', ', $columns);
93 return $prefix . '_' . join(', ' . $prefix . '_', $columns);
96 private function prefixed_name($column, $prefix = NULL) {
97 return $prefix ? $prefix . '_' . $column : $column;
100 private function prepare_params($params, &$placeholders, &$values) {
101 $column_names = array_keys($params);
103 $i = count($values) + 1;
104 foreach ($column_names as $name) {
105 assert(ctype_alnum_underscore($name));
106 array_push($placeholders, '$' . $i);
107 array_push($values, $params[$name]);
111 return $column_names;
114 function insert_row($table, $prefix, $params, $returning = 'id') {
115 $placeholders = array();
117 $column_names = $this->prepare_params($params, $placeholders, $values);
119 assert(!$prefix || ctype_alnum_underscore($prefix));
120 $column_names = $this->prefixed_column_names($column_names, $prefix);
121 $placeholders = join(', ', $placeholders);
123 $value_query = $column_names ? "($column_names) VALUES ($placeholders)" : ' VALUES (default)';
125 $returning_column_name = $this->prefixed_name($returning, $prefix);
126 $rows = $this->query_and_fetch_all("INSERT INTO $table $value_query RETURNING $returning_column_name", $values);
127 return $rows ? $rows[0][$returning_column_name] : NULL;
130 return $this->query_and_get_affected_rows("INSERT INTO $table $value_query", $values) == 1;
133 function select_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, FALSE, TRUE);
137 function update_or_insert_row($table, $prefix, $select_params, $insert_params = NULL, $returning = 'id') {
138 return $this->_select_update_or_insert_row($table, $prefix, $select_params, $insert_params, $returning, TRUE, TRUE);
141 function update_row($table, $prefix, $select_params, $update_params, $returning = 'id') {
142 return $this->_select_update_or_insert_row($table, $prefix, $select_params, $update_params, $returning, TRUE, FALSE);
145 private function _select_update_or_insert_row($table, $prefix, $select_params, $insert_params, $returning, $should_update, $should_insert) {
148 $select_placeholders = array();
149 $select_column_names = $this->prepare_params($select_params, $select_placeholders, $values);
150 $select_values = array_slice($values, 0);
152 if ($insert_params === NULL)
153 $insert_params = $select_params;
154 $insert_placeholders = array();
155 $insert_column_names = $this->prepare_params($insert_params, $insert_placeholders, $values);
157 assert(!!$returning);
158 assert(!$prefix || ctype_alnum_underscore($prefix));
159 $returning_column_name = $returning == '*' ? '*' : $this->prefixed_name($returning, $prefix);
160 $select_column_names = $this->prefixed_column_names($select_column_names, $prefix);
161 $select_placeholders = join(', ', $select_placeholders);
162 $query = "SELECT $returning_column_name FROM $table WHERE ($select_column_names) = ($select_placeholders)";
164 $insert_column_names = $this->prefixed_column_names($insert_column_names, $prefix);
165 $insert_placeholders = join(', ', $insert_placeholders);
167 // http://stackoverflow.com/questions/1109061/insert-on-duplicate-update-in-postgresql
169 if ($should_update) {
170 $rows = $this->query_and_fetch_all("UPDATE $table SET ($insert_column_names) = ($insert_placeholders)
171 WHERE ($select_column_names) = ($select_placeholders) RETURNING $returning_column_name", $values);
173 if (!$rows && $should_insert) {
174 $rows = $this->query_and_fetch_all("INSERT INTO $table ($insert_column_names) SELECT $insert_placeholders
175 WHERE NOT EXISTS ($query) RETURNING $returning_column_name", $values);
177 if (!$should_update && !$rows)
178 $rows = $this->query_and_fetch_all($query, $select_values);
180 return $rows ? ($returning == '*' ? $rows[0] : $rows[0][$returning_column_name]) : NULL;
183 function select_first_row($table, $prefix, $params, $order_by = NULL) {
184 return $this->select_first_or_last_row($table, $prefix, $params, $order_by, FALSE);
187 function select_last_row($table, $prefix, $params, $order_by = NULL) {
188 return $this->select_first_or_last_row($table, $prefix, $params, $order_by, TRUE);
191 private function select_first_or_last_row($table, $prefix, $params, $order_by, $descending_order) {
192 $rows = $this->select_rows($table, $prefix, $params, $order_by, $descending_order, 0, 1);
193 return $rows ? $rows[0] : NULL;
196 function select_rows($table, $prefix, $params,
197 $order_by = NULL, $descending_order = FALSE, $offset = NULL, $limit = NULL) {
199 $placeholders = array();
201 $column_names = $this->prefixed_column_names($this->prepare_params($params, $placeholders, $values), $prefix);
202 $placeholders = join(', ', $placeholders);
203 if (!$column_names && !$placeholders)
204 $column_names = $placeholders = '1';
205 $query = "SELECT * FROM $table WHERE ($column_names) = ($placeholders)";
207 assert(ctype_alnum_underscore($order_by));
208 $query .= ' ORDER BY ' . $this->prefixed_name($order_by, $prefix);
209 if ($descending_order)
212 if ($offset !== NULL)
213 $query .= ' OFFSET ' . intval($offset);
215 $query .= ' LIMIT ' . intval($limit);
217 return $this->query_and_fetch_all($query, $values);
220 function query_and_get_affected_rows($query, $params = array()) {
221 if (!$this->connection)
223 $result = pg_query_params($this->connection, $query, $params);
226 return pg_affected_rows($result);
229 function query_and_fetch_all($query, $params = array()) {
230 if (!$this->connection)
232 $result = pg_query_params($this->connection, $query, $params);
235 if (pg_num_rows($result) == 0)
237 return pg_fetch_all($result);
240 function query($query, $params = array()) {
241 if (!$this->connection)
243 return pg_query_params($this->connection, $query, $params);
246 function fetch_next_row($result) {
247 return pg_fetch_assoc($result);
250 function fetch_table($table_name, $column_to_be_ordered_by = null) {
251 if (!$this->connection || !ctype_alnum_underscore($table_name) || ($column_to_be_ordered_by && !ctype_alnum_underscore($column_to_be_ordered_by)))
254 if ($column_to_be_ordered_by)
255 $clauses .= 'ORDER BY ' . $column_to_be_ordered_by;
256 return $this->query_and_fetch_all("SELECT * FROM $table_name $clauses");
259 function begin_transaction() {
260 return $this->connection and pg_query($this->connection, "BEGIN");
263 function commit_transaction() {
264 return $this->connection and pg_query($this->connection, 'COMMIT');
267 function rollback_transaction() {
268 return $this->connection and pg_query($this->connection, 'ROLLBACK');