--- a/ChangeLog Thu May 05 10:50:51 2011 +0200
+++ b/ChangeLog Thu May 05 11:19:59 2011 +0200
@@ -2,7 +2,9 @@
=================
--
-* suport != operator for non equality
+* support != operator for non equality
+* support for CAST function
+* support for regexp-based pattern matching using a REGEXP operator
2011-01-12 -- 0.28.0
* enhance rewrite_shared_optional so one can specify where the new identity
--- a/__pkginfo__.py Thu May 05 10:50:51 2011 +0200
+++ b/__pkginfo__.py Thu May 05 11:19:59 2011 +0200
@@ -81,7 +81,7 @@
install_requires = [
'logilab-common >= 0.47.0',
- 'logilab-database',
+ 'logilab-database >= 1.6.0',
'yapps == 2.1.1', # XXX to ensure we don't use the broken pypi version
'constraint', # fallback if the gecode compiled module is missing
]
--- a/debian/control Thu May 05 10:50:51 2011 +0200
+++ b/debian/control Thu May 05 11:19:59 2011 +0200
@@ -11,7 +11,7 @@
Package: python-rql
Architecture: any
XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, ${misc:Depends}, ${shlibs:Depends}, python-logilab-common (>= 0.35.3-1), yapps2-runtime, python-logilab-database
+Depends: ${python:Depends}, ${misc:Depends}, ${shlibs:Depends}, python-logilab-common (>= 0.35.3-1), yapps2-runtime, python-logilab-database (>= 1.6.0)
Conflicts: cubicweb-common (<= 3.8.3)
Provides: ${python:Provides}
Description: relationship query language (RQL) utilities
--- a/nodes.py Thu May 05 10:50:51 2011 +0200
+++ b/nodes.py Thu May 05 11:19:59 2011 +0200
@@ -28,6 +28,8 @@
from datetime import datetime, date, time, timedelta
from time import localtime
+from logilab.database import DYNAMIC_RTYPE
+
from rql import CoercionError
from rql.base import BaseNode, Node, BinaryNode, LeafNode
from rql.utils import (function_description, quote, uquote, build_visitor_stub,
@@ -440,12 +442,7 @@
return False
rhs = self.children[1]
if isinstance(rhs, Comparison):
- try:
- rhs = rhs.children[0]
- except:
- print 'opppp', rhs
- print rhs.root
- raise
+ rhs = rhs.children[0]
# else: relation used in SET OR DELETE selection
return ((isinstance(rhs, Constant) and rhs.type == 'etype')
or (isinstance(rhs, Function) and rhs.name == 'IN'))
@@ -484,7 +481,7 @@
self.optional= value
-OPERATORS = frozenset(('=', '!=', '<', '<=', '>=', '>', 'ILIKE', 'LIKE'))
+OPERATORS = frozenset(('=', '!=', '<', '<=', '>=', '>', 'ILIKE', 'LIKE', 'REGEXP'))
class Comparison(HSMixin, Node):
"""handle comparisons:
@@ -625,7 +622,8 @@
solution is an optional variable/etype mapping
"""
- rtype = self.descr().rtype
+ func_descr = self.descr()
+ rtype = func_descr.rql_return_type(self)
if rtype is None:
# XXX support one variable ref child
try:
--- a/parser.g Thu May 05 10:50:51 2011 +0200
+++ b/parser.g Thu May 05 11:19:59 2011 +0200
@@ -88,7 +88,7 @@
token FALSE: r'(?i)FALSE'
token NULL: r'(?i)NULL'
token EXISTS: r'(?i)EXISTS'
- token CMP_OP: r'(?i)<=|<|>=|>|!=|=|~=|LIKE|ILIKE'
+ token CMP_OP: r'(?i)<=|<|>=|>|!=|=|~=|LIKE|ILIKE|REGEXP'
token ADD_OP: r'\+|-'
token MUL_OP: r'\*|/'
token FUNCTION: r'[A-Za-z_]+\s*(?=\()'
--- a/parser.py Thu May 05 10:50:51 2011 +0200
+++ b/parser.py Thu May 05 11:19:59 2011 +0200
@@ -1,22 +1,8 @@
-# copyright 2004-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of rql.
-#
-# rql is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# rql is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with rql. If not, see <http://www.gnu.org/licenses/>.
"""yapps input grammar for RQL.
+:organization: Logilab
+:copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
Select statement grammar
@@ -109,7 +95,7 @@
('FALSE', re.compile('(?i)FALSE')),
('NULL', re.compile('(?i)NULL')),
('EXISTS', re.compile('(?i)EXISTS')),
- ('CMP_OP', re.compile('(?i)<=|<|>=|>|!=|=|~=|LIKE|ILIKE')),
+ ('CMP_OP', re.compile('(?i)<=|<|>=|>|!=|=|~=|LIKE|ILIKE|REGEXP')),
('ADD_OP', re.compile('\\+|-')),
('MUL_OP', re.compile('\\*|/')),
('FUNCTION', re.compile('[A-Za-z_]+\\s*(?=\\()')),
@@ -709,7 +695,7 @@
P = Hercule(HerculeScanner(text))
return runtime.wrap_error_reporter(P, rule)
-if __name__ == 'old__main__':
+if __name__ == '__main__':
from sys import argv, stdin
if len(argv) >= 2:
if len(argv) >= 3:
@@ -719,25 +705,3 @@
print parse(argv[1], f.read())
else: print >>sys.stderr, 'Args: <rule> [<filename>]'
# End -- grammar generated by Yapps
-"""Main parser command.
-
-"""
-__docformat__ = "restructuredtext en"
-
-if __name__ == '__main__':
- from sys import argv
-
- parser = Hercule(HerculeScanner(argv[1]))
- e_types = {}
- # parse the RQL string
- try:
- tree = parser.goal(e_types)
- print '-'*80
- print tree
- print '-'*80
- print repr(tree)
- print e_types
- except SyntaxError, ex:
- # try to get error message from yapps
- from yapps.runtime import print_error
- print_error(ex, parser._scanner)
--- a/stcheck.py Thu May 05 10:50:51 2011 +0200
+++ b/stcheck.py Thu May 05 11:19:59 2011 +0200
@@ -455,11 +455,16 @@
def visit_constant(self, constant, state):
#assert len(constant.children)==0
if constant.type == 'etype':
- if constant.relation().r_type not in ('is', 'is_instance_of'):
- msg ='using an entity type in only allowed with "is" relation'
- state.error(msg)
- if not constant.value in self.schema:
+ if constant.value not in self.schema:
state.error('unknown entity type %s' % constant.value)
+ rel = constant.relation()
+ if rel is not None:
+ if rel.r_type not in ('is', 'is_instance_of'):
+ msg ='using an entity type in only allowed with "is" relation'
+ state.error(msg)
+ elif not (isinstance(constant.parent, Function) and
+ constant.parent.name == 'CAST'):
+ state.error('Entity types can only be used inside a CAST()')
def leave_constant(self, node, state):
pass
--- a/test/unittest_nodes.py Thu May 05 10:50:51 2011 +0200
+++ b/test/unittest_nodes.py Thu May 05 11:19:59 2011 +0200
@@ -564,6 +564,12 @@
tree = sparse('Any X,R,D,Y WHERE X work_for R, R creation_date D, Y connait X')
self.assertEqual(tree.get_description(0), [['Person, Student', 'work_for', 'creation_date', 'connait']])
+ def test_get_description_cast(self):
+ tree = sparse('Any CAST(String, Y) WHERE X creation_date Y')
+ select = tree.children[0]
+ self.assertEqual(select.selection[0].get_type(), 'String')
+ self.assertEqual(tree.get_description(0), [['String']])
+
class GetNodesFunctionTest(TestCase):
def test_known_values_1(self):
--- a/utils.py Thu May 05 10:50:51 2011 +0200
+++ b/utils.py Thu May 05 11:19:59 2011 +0200
@@ -68,7 +68,7 @@
from logilab.common.decorators import monkeypatch
-from logilab.database import SQL_FUNCTIONS_REGISTRY, FunctionDescr
+from logilab.database import SQL_FUNCTIONS_REGISTRY, FunctionDescr, CAST
RQL_FUNCTIONS_REGISTRY = SQL_FUNCTIONS_REGISTRY.copy()
@@ -85,6 +85,19 @@
raise BadRQLQuery("backend %s doesn't support function %s" % (backend, self.name))
+@monkeypatch(FunctionDescr)
+def rql_return_type(self, funcnode):
+ return self.rtype
+
+@monkeypatch(CAST)
+def st_description(self, funcnode, mainindex, tr):
+ return self.rql_return_type(funcnode)
+
+@monkeypatch(CAST)
+def rql_return_type(self, funcnode):
+ return funcnode.children[0].value
+
+
def iter_funcnode_variables(funcnode):
for term in funcnode.children:
try: