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

Last change on this file since 312 was 312, checked in by jkraaijeveld, 13 years ago
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.