source: Dev/branches/rest-dojo-ui/server/rdfapi/sparql/SparqlEngineDb.php @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 12.0 KB
Line 
1<?php
2require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngine.php';
3require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/Offsetter.php';
4require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/QuerySimplifier.php';
5require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/ResultConverter.php';
6require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/SqlGenerator.php';
7require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/SqlMerger.php';
8
9/**
10*   SPARQL engine optimized for databases.
11*   Generates SQL statements to directly query the database,
12*   letting the database system do all the hard work like
13*   selecting, joining, filtering and ordering results.
14*
15*   @author Christian Weiske <cweiske@cweiske.de>
16*   @license http://www.gnu.org/licenses/lgpl.html LGPL
17*
18*   @package sparql
19*/
20class SparqlEngineDb
21{
22    /**
23    *   Sparql Query object.
24    *
25    *   @var Query
26    */
27    protected $query;
28
29    /**
30    *   RDF dataset object.
31    *   @var Dataset
32    */
33    protected $dataset;
34
35    /**
36    *   Database connection object.
37    *   @var ADOConnection
38    */
39    protected $dbConn;
40
41    /**
42    *   Internal ID for our graph model.
43    *   Stored in the database along the statements.
44    *   Can be of different types:
45    *   - array: array of modelIds
46    *   - null: all models
47    *
48    *   @var array OR null
49    */
50    protected $arModelIds;
51
52    /**
53    *   Prepared SQL statements are stored in here.
54    *   @var array
55    */
56    protected $arPrepared    = null;
57
58    /**
59    *   If the prepared statement is really prepared, or if we just emulate it.
60    *   @var boolean
61    */
62    protected $bRealPrepared = false;
63
64    /**
65    *   SQL generator instance
66    *   @var SparqlEngineDb_SqlGenerator
67    */
68    protected $sg = null;
69
70    /**
71    *   Type sorting instance
72    *   @var SparqlEngineDb_TypeSorter
73    */
74    protected $ts = null;
75
76    /**
77    *   Prepared statments preparator instance
78    *   @var SparqlEngineDb_Preparator
79    */
80    protected $pr = null;
81
82
83
84    /**
85    *   Use SparqlEngine::factory() to create the instance
86    *
87    *   @param mixed $model         DbModel or DbStore
88    *   @param mixed $arModelIds    Array of modelIds, or NULL to use all models
89    */
90    public function __construct($model, $arModelIds = null)
91    {
92        //parent::__construct();
93        if ($model instanceof DbModel) {
94            $this->dbConn     = $model->getDbConn();
95            $this->arModelIds = array($model->getModelID());
96        } else if ($model instanceof DbStore) {
97            $this->dbConn     = $model->getDbConn();
98            $this->arModelIds = $arModelIds;
99        }
100    }//public function __construct(DbModel $model, $arModelIds = null)
101
102
103
104    /**
105    *   Query the database with the given SPARQL query.
106    *
107    *
108    *   @param  Dataset       $dataset    RDF Dataset
109    *   @param  Query         $query      Parsed SPARQL query
110    *   @param  string        $resultform Result form. If set to 'xml' the result will be
111    *                                   SPARQL Query Results XML Format as described in http://www.w3.org/TR/rdf-sparql-XMLres/ .
112    *
113    *   @return array/string  array of triple arrays, or XML. Format depends on
114    *                                   $resultform parameter.
115    */
116    public function queryModel($dataset, Query $query, $resultform = false)
117    {
118        if (isset($GLOBALS['debugSparql']) && $GLOBALS['debugSparql']) {
119            echo "\n" . 'SPARQL query: ' . $query->getQueryString() . "\n";
120        }
121
122        $this->query   = $query;
123        $this->dataset = $dataset;
124
125        $qsimp = new SparqlEngineDb_QuerySimplifier();
126        $qsimp->simplify($this->query);
127
128        $this->sg = new SparqlEngineDb_SqlGenerator   ($this->query, $this->dbConn, $this->arModelIds);
129        $this->rc = new SparqlEngineDb_ResultConverter($this->query, $this->sg, $this);
130        $this->ts = new SparqlEngineDb_TypeSorter     ($this->query, $this->dbConn);
131
132        $this->setOptions();
133
134        if ($this->query->isEmpty()){
135            $vartable[0]['patternResult'] = null;
136            return $this->returnResult($vartable, $resultform);
137        }
138
139
140        $arSqls = $this->sg->createSql();
141        $this->ts->setData($this->sg);
142
143        return
144            SparqlEngineDb_ResultConverter::convertFromDbResults(
145                $this->queryMultiple(
146                    $this->ts->getOrderifiedSqls(
147                        $arSqls
148                    )
149                ),
150                $this,
151                $resultform
152            );
153    }//public function queryModel($dataset, Query $query, $resultform = false)
154
155
156
157    /**
158    *   Create a prepared statement that can be executed later.
159    *
160    *   @param  Dataset       $dataset    RDF Dataset
161    *   @param  Query         $query      Parsed SPARQL query
162    *
163    *   @return SparqlEngineDb_PreparedStatement Prepared statment that can
164    *           be execute()d later.
165    */
166    public function prepare(Dataset $dataset, Query $query)
167    {
168        require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/PreparedStatement.php';
169        require_once RDFAPI_INCLUDE_DIR . 'sparql/SparqlEngineDb/Preparator.php';
170
171        $this->query   = $query;
172        $this->dataset = $dataset;
173        $this->sg = new SparqlEngineDb_SqlGenerator   ($this->query, $this->dbConn, $this->arModelIds);
174        $this->rc = new SparqlEngineDb_ResultConverter($this->query, $this->sg, $this);
175        $this->ts = new SparqlEngineDb_TypeSorter     ($this->query, $this->dbConn);
176        $this->pr = new SparqlEngineDb_Preparator     ($this->query, $this->dbConn);
177
178        $this->arPrepared = $this->sg->createSql();
179        $this->ts->setData($this->sg);
180
181        if ($this->ts->willBeDataDependent()) {
182            $this->bRealPrepared = false;
183        } else {
184            $this->bRealPrepared     = true;
185            list($strSelect, $strFrom, $strWhere) = $this->arPrepared;
186            $this->arPreparedQueries = $this->ts->getOrderifiedSqls(
187                $strSelect,
188                $strFrom,
189                $strWhere
190            );
191            $this->arDbStatements    = $this->pr->prepareInDb(
192                $this->arPreparedQueries,
193                $this->sg->getPlaceholders()
194            );
195        }
196
197
198        return new SparqlEngineDb_PreparedStatement(
199            $this
200        );
201    }//public function prepare(Dataset $dataset, Query $query)
202
203
204
205    /**
206    *   Execute a prepared statement by filling it with variables
207    *
208    *   @param array $arVariables   Array with (variable name => value) pairs
209    *   @param string $resultform   Which form the result should have
210    *
211    *   @return mixed   Result according to $resultform
212    */
213    public function execute($arVariables, $resultform = false)
214    {
215        if ($this->arPrepared === null) {
216            throw new Exception('You need to prepare() the query first.');
217        }
218
219        if ($this->bRealPrepared) {
220            return
221                SparqlEngineDb_ResultConverter::convertFromDbResults(
222                    $this->pr->execute(
223                        $this->arDbStatements,
224                        $arVariables
225                    ),
226                    $this,
227                    $resultform
228                );
229        } else {
230            list($strSelect, $strFrom, $strWhere) = $this->arPrepared;
231
232            return SparqlEngineDb_ResultConverter::convertFromDbResults(
233                $this->queryMultiple(
234                    $this->ts->getOrderifiedSqls(
235                        $strSelect,
236                        $strFrom,
237                        $this->pr->replacePlaceholdersWithVariables(
238                            $strWhere,
239                            $this->sg->getPlaceholders(),
240                            $arVariables
241                        )
242                    )
243                ),
244                $this,
245                $resultform
246            );
247        }
248    }//public function execute($arVariables, $resultform)
249
250
251
252    /**
253    *   Executes multiple SQL queries and returns an array
254    *   of results.
255    *
256    *   @param array $arSqls     Array of SQL queries
257    *   @return array   Array of query results
258    */
259    protected function queryMultiple($arSqls)
260    {
261        $arSM = $this->query->getSolutionModifier();
262        if ($arSM['limit'] === null && $arSM['offset'] === null) {
263            $nOffset = 0;
264            $nLimit  = null;
265            $nSql    = 0;
266        } else {
267            $offsetter = new SparqlEngineDb_Offsetter($this->dbConn, $this->query);
268            list($nSql, $nOffset) = $offsetter->determineOffset($arSqls);
269            $nLimit    = $arSM['limit'];
270        }
271
272        $nCount    = 0;
273        $arResults = array();
274        foreach ($arSqls as $nId => $arSql) {
275            if ($nId < $nSql) { continue; }
276
277            if ($nLimit != null) {
278                $nCurrentLimit = $nLimit - $nCount;
279            } else {
280                $nCurrentLimit = null;
281            }
282
283            $dbResult    = $this->queryDb($arSql, $nOffset, $nCurrentLimit);
284            $nCount     += $dbResult->RowCount();
285            $arResults[] = $dbResult;
286            $nOffset = 0;
287            if ($nLimit !== null && $nCount >= $nLimit) {
288                break;
289            }
290        }
291
292        return $arResults;
293        //return array_map(array($this, 'queryDb'), $arSql);
294    }//protected function queryMultiple($arSql)
295
296
297
298    /**
299    *   Sends the sql to the database and returns the results.
300    *
301    *   @internal Switches between ADOConnection::Execute() and
302    *    ADOConnection::SelectLimit() depending on the $query parameter's
303    *    $solutionModifier "limit" and "offset" settings.
304    *   Uses $query variable.
305    *
306    *   @param array $arSql     Array that gets a SQL query string once imploded
307    *
308    *   @return mixed           Anything ADOConnection::Execute() may return
309    *   @throws Exception       If Database query does not work
310    */
311    function queryDb($arSql, $nOffset, $nLimit)
312    {
313        $strSql = SparqlEngineDb_SqlMerger::getSelect($this->query, $arSql);
314
315        if ($strSql == '()') {
316            return new ADORecordSet(false);
317        }
318
319        // I want associative arrays.
320        $oldmode = $this->dbConn->SetFetchMode(ADODB_FETCH_ASSOC);
321
322        if (isset($GLOBALS['debugSparql']) && $GLOBALS['debugSparql']) {
323            echo 'SQL query: ' . $strSql . "\n";
324        }
325
326        if ($nLimit === null && $nOffset == 0) {
327            $ret = $this->dbConn->execute($strSql);
328        } else if ($nLimit === null) {
329            $ret = $this->dbConn->SelectLimit($strSql, -1, $nOffset);
330        } else {
331            $ret = $this->dbConn->SelectLimit($strSql, $nLimit, $nOffset);
332        }
333
334        //... but others maybe not
335        $this->dbConn->SetFetchMode($oldmode);
336
337        if (!$ret) {
338            //Error occured
339            throw new Exception(
340                'ADOdb error: ' . $this->dbConn->ErrorMsg() . "\n"
341                . $strSql
342            );
343        }
344
345        return $ret;
346    }//function queryDb($sql)
347
348
349
350    /**
351    *   Set options to subobjects like SqlGenerator
352    */
353    protected function setOptions()
354    {
355        //allow changing the statements' table name
356        if (isset($GLOBALS['RAP']['conf']['database']['tblStatements'])) {
357            $this->sg->setStatementsTable(
358                $GLOBALS['RAP']['conf']['database']['tblStatements']
359            );
360        }
361    }//protected function setOptions()
362
363
364
365    /*
366    *   Dumb getters
367    */
368
369
370
371    public function getQuery()
372    {
373        return $this->query;
374    }//public function getQuery()
375
376
377
378    public function getSqlGenerator()
379    {
380        return $this->sg;
381    }//public function getSqlGenerator()
382
383
384
385    public function getTypeSorter()
386    {
387        return $this->ts;
388    }//public function getTypeSorter()
389
390}//class SparqlEngineDb
391?>
Note: See TracBrowser for help on using the repository browser.