4 * Класс преобразует RQL-запросы в язык SQL
12 if (!function_exists('__autoload')) {
13 include dirname(__FILE__
).'/cercea/XMLConvertable.php';
14 include dirname(__FILE__
).'/cercea/JSONConvertable.php';
15 include dirname(__FILE__
).'/cercea/Set.php';
16 include dirname(__FILE__
).'/csda/RecordType.php';
17 include dirname(__FILE__
).'/csda/Record.php';
18 include dirname(__FILE__
).'/csda/RecordSet.php';
19 include dirname(__FILE__
).'/csda/MixedRecord.php';
20 include dirname(__FILE__
).'/RQLAutomato.php';
21 include dirname(__FILE__
).'/RQLAutomatoState.php';
22 include dirname(__FILE__
).'/RQLDescription.php';
23 include dirname(__FILE__
).'/RQLNode.php';
24 include dirname(__FILE__
).'/RQLFork.php';
25 include dirname(__FILE__
).'/RQLPath.php';
33 protected static $cache = false;
34 protected static $stype = 'cercea';
35 protected static $sid = 1;
36 protected static $slock = false;
41 const ACL_CHILD_APPEND
= 16;
42 const ACL_CHILD_OPERATION
= 32;
43 const ACL_RELATION_OPERATION
= 64;
49 protected static $connection;
50 static function destroy_nodes($type, $ids) {
51 $ids = array_unique($ids);
52 $query = "DELETE FROM {$type} WHERE `id` IN (".implode(',',$ids).")";
54 $query = "DELETE FROM `relations` WHERE child = '{$type}' and `child_id` IN (".implode(',',$ids).")";
58 static protected function query($q) {
59 if (!self
::$connection->query($q)) {
60 throw new Exception(self
::$connection->error
."<br/><br/>\r\n\r\n$q<br/><br/>\r\n\r\n".self
::$connection->error
);
65 static function exec_delete($query, $order = null, $limit = null) {
67 if ($order !== null) {
68 $query .= ' ORDER BY '.$order;
70 if ($limit !== null) {
71 $query .= ' LIMIt '.$limit;
74 if ($res = self
::$connection->query($query)) {
75 while($row = $res->fetch_array(MYSQLI_NUM
)) {
76 for($i=0;$i<count($row);$i=$i+
2) {
77 $delete[$row[$i]][] = $row[$i+
1];
80 foreach($delete as $table => $ids) {
81 self
::drop_nodes($table, $ids);
84 throw new Exception(self
::$connection->error
."<br/><br/>\r\n\r\n$query<br/><br/>\r\n\r\n".self
::$connection->error
);
88 static function exec_select($query, $types, $order = null, $limit = null) {
89 if ($order !== null) {
90 $query .= ' ORDER BY '.$order;
92 if ($limit !== null) {
93 $query .= ' LIMIT '.$limit;
95 $res = self
::$connection->query($query);
96 if (!$res) return false;
98 $ret = new RecordSet($res, $types, $res->field_count
);
99 } catch (exception
$e) {
100 if ($e->getCode() == 1011) return false;
108 * @param <type> $query
109 * @param <type> $order
110 * @param none $limit ignored
113 static function exec_update($query, $order = null, $limit = null) {
115 if ($order !== null) {
116 $query .= ' ORDER BY '.$order;
118 if (!self
::$connection->query($query)) {
119 throw new Exception(self
::$connection->error
."<br/><br/>\r\n\r\n$query<br/><br/>\r\n\r\n".self
::$connection->error
);
124 function __construct($host = null, $user = null, $password = null, $database = null, $names = null) {
126 // initializing connection parameters
127 if (class_exists('X') && ($host === null ||
$names === null)) {
128 // Config is a Xendri Framework module from a base package
129 if ($host === null) {
130 $host = X
::cfg('sql:host');
131 $user = X
::cfg('sql:user');
132 $password = X
::cfg('sql:password');
133 $database = X
::cfg('sql:database');
136 if ($names === null) {
137 $names = X
::cfg('sql:names');
142 self
::$connection = new mysqli($host, $user, $password, $database);
143 if (mysqli_connect_errno()) {
144 throw new Exception('Connection to database failed!');
148 public function get_sql() {
149 return $this->mk_sql($this->sql
);
152 protected function translate($rql) {
153 $automato = new RqlAutomato($rql);
155 $res = $automato->result();
159 public static function from($rql) {
160 if (self
::$connection == null) throw new Exception('You MUST create at least one object of class RQL!');
161 $chk7 = $this->checksum
= md5($rql);
162 if (!self
::$cache) self
::$cache = array();
164 if (!key_exists($chk, self
::$cache)) {
165 $automato = new RqlAutomato($rql);
167 $res = $automato->result();
169 self
::$cache[$chk] = $this->sql
;
177 * @param string $rql RQL-query to database
178 * @param string $order affected rows sorting options
179 * @param string $limit affected rows limit options
182 public static function exec($rql, $order = null, $limit = null) {
183 if (self
::$connection == null) throw new Exception('You MUST create at least one object of class RQL!');
185 if (strpos(trim($rql), 'self') != 0) {
186 $rql = 'self / '.$rql;
189 $rql = str_replace('self', (self
::$stype).'[id='.self
::$sid.']', $rql);
191 $automato = new RqlAutomato($rql);
193 $res = $automato->result();
196 echo "inserts: ".print_r($res->get_insert_queries(), 1)."\r\n";
198 $insertion_result = self
::exec_inserts($res->get_insert_queries());
200 for($i=0;$i<count($insertion_result); $i++
) {
201 $res->update_description_by_real_name($insertion_result[$i]['name'], 'id', '=', $insertion_result[$i]['id']);
202 // $res->drop_insertion_by_real_name($insertion_result[$i]['name']);
205 echo "update: ".$res->get_update_query()."\r\n";
206 self
::exec_update($res->get_update_query(), $order, $limit);
208 echo "select: ".$res->get_select_query()."\r\n";
209 $sel = self
::exec_select($res->get_select_query(), $res->get_selected_real_names(), $order, $limit);
210 self
::exec_delete($res->get_delete_query(), $order, $limit);
215 protected static function exec_inserts($inserts) {
216 $holder = '##**RQL_NEW_NODE_ID_PLACEHOLDER**##';
218 for($i=0;$i<count($inserts);$i++
) {
219 $insert = $inserts[$i];
220 $query = $insert['check'];
221 $names = $insert['insert']['parent_type'];
222 if (!is_array($names)) {
223 $names = array($names);
225 $names = array_unique($names);
229 if ($res = self
::$connection->query($query)) {
230 while(true == ($row = $res->fetch_array())) {
231 $ids[$row[0]][] = $row[1];
232 $values[] = "( 255, '{$row[0]}', '{$row[1]}', '{$insert['insert']}', '{$holder}')";
235 throw new Exception(self
::$connection->error
."<br/><br/>\r\n\r\n$query<br/><br/>\r\n\r\n".self
::$connection->error
);
237 if (count($values) == 0) throw new Exception('There was no parents available for new node `'.$insert['insert'].'` (Yoda says: privileges insufficient maybe?)!');
238 $query = "INSERT INTO `{$insert['insert']}` (`id`) VALUES ('')";
239 if($res = self
::$connection->query($query)) {
240 $relations = "INSERT INTO `relations` (`acl`, `parent`, `parent_id`, `child`, `child_id`) VALUES ".implode(',', $values);
241 $relations = str_replace($holder, self
::$connection->insert_id
, $relations);
243 'name' => $insert['insert'],
244 'id' => self
::$connection->insert_id
246 if (!self
::$connection->query($relations)) {
247 throw new Exception(self
::$connection->error
."<br/><br/>\r\n\r\n$query<br/><br/>\r\n\r\n".self
::$connection->error
);
250 throw new Exception(self
::$connection->error
."<br/><br/>\r\n\r\n$query<br/><br/>\r\n\r\n".self
::$connection->error
);
258 protected function drop_nodes($type, $ids) {
259 $ids = array_unique($ids);
260 $query = "UPDATE `relations` SET `parent` = '---deleted---', `parent_id` = -1 WHERE (`child` = '{$type}' AND `child_id` IN (".implode(',',$ids).")) OR (`parent` = '{$type}' AND `parent_id` IN (".implode(',',$ids)."))";
261 if (!self
::$connection->query($query)) {
262 throw new Exception(self
::$connection->error
."<br/><br/>\r\n\r\n$query<br/><br/>\r\n\r\n".self
::$connection->error
);
264 self
::clear_database();
268 * clears deleted nodes from database
270 protected static function clear_database() {
271 $query = 'select distinct deleted.child, deleted.child_id '.
272 'from relations as deleted '.
273 'left join relations as remains ON '.
274 'remains.child_id = deleted.child_id AND '.
275 'remains.child = deleted.child AND '.
276 'remains.parent_id != -1 '.
277 'where deleted.parent_id = -1 and coalesce(remains.parent_id, 0) = 0 '.
278 'group by deleted.child, deleted.child_id';
279 $result = self
::$connection->query($query);
281 throw new Exception(self
::$connection->error
."<br/><br/>\r\n\r\n$query<br/><br/>\r\n\r\n".self
::$connection->error
);
285 while($row = $result->fetch_array(MYSQLI_NUM
)) {
286 $drops[$row[0]][] = $row[1];
291 foreach ($drops as $type=>$ids) {
292 self
::destroy_nodes($type, $ids);
295 if(count($drops) > 0) self
::clear_database();
298 static function get_connection() {
299 return self
::$connection;
302 static function set_self(Record
$rec, $lock = true) {
303 if (self
::$slock) return false;
304 self
::$slock = $lock;
305 self
::$stype = $rec->get_type();
306 self
::$sid = $rec->id();