{"action":"created","issue":{"url":"https://api.github.com/repos/rogerbinns/apsw/issues/196","labels_url":"https://api.github.com/repos/rogerbinns/apsw/issues/196/labels{/name}","comments_url":"https://api.github.com/repos/rogerbinns/apsw/issues/196/comments","events_url":"https://api.github.com/repos/rogerbinns/apsw/issues/196/events","html_url":"https://github.com/rogerbinns/apsw/issues/196","id":119537915,"number":196,"title":"Support for eponymous virtual tables","user":{"login":"coleifer","id":119974,"avatar_url":"https://avatars.githubusercontent.com/u/119974?v=3","gravatar_id":"","url":"https://api.github.com/users/coleifer","html_url":"https://github.com/coleifer","followers_url":"https://api.github.com/users/coleifer/followers","following_url":"https://api.github.com/users/coleifer/following{/other_user}","gists_url":"https://api.github.com/users/coleifer/gists{/gist_id}","starred_url":"https://api.github.com/users/coleifer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coleifer/subscriptions","organizations_url":"https://api.github.com/users/coleifer/orgs","repos_url":"https://api.github.com/users/coleifer/repos","events_url":"https://api.github.com/users/coleifer/events{/privacy}","received_events_url":"https://api.github.com/users/coleifer/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":{"login":"rogerbinns","id":553885,"avatar_url":"https://avatars.githubusercontent.com/u/553885?v=3","gravatar_id":"","url":"https://api.github.com/users/rogerbinns","html_url":"https://github.com/rogerbinns","followers_url":"https://api.github.com/users/rogerbinns/followers","following_url":"https://api.github.com/users/rogerbinns/following{/other_user}","gists_url":"https://api.github.com/users/rogerbinns/gists{/gist_id}","starred_url":"https://api.github.com/users/rogerbinns/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rogerbinns/subscriptions","organizations_url":"https://api.github.com/users/rogerbinns/orgs","repos_url":"https://api.github.com/users/rogerbinns/repos","events_url":"https://api.github.com/users/rogerbinns/events{/privacy}","received_events_url":"https://api.github.com/users/rogerbinns/received_events","type":"User","site_admin":false},"milestone":null,"comments":3,"created_at":"2015-11-30T17:17:06Z","updated_at":"2015-11-30T18:58:20Z","closed_at":null,"body":"An eponymous virtual table is defined as a virtual table whose `xCreate` is the same as `xConnect` or `xCreate` is `NULL` ([docs](https://www.sqlite.org/vtab.html#epovtab)).\r\n\r\nFrom my reading of the apsw vtable source code, the xCreate and xConnect method pointers are hard-coded and thus it doesn't appear possible to me to implement such a virtual table using APSW.\r\n\r\nI would suggest a change that allows the `VTModule.Create` to be set to `None` or some other sentinel value to signal that this virtual table is eponymous."},"comment":{"url":"https://api.github.com/repos/rogerbinns/apsw/issues/comments/160724901","html_url":"https://github.com/rogerbinns/apsw/issues/196#issuecomment-160724901","issue_url":"https://api.github.com/repos/rogerbinns/apsw/issues/196","id":160724901,"user":{"login":"coleifer","id":119974,"avatar_url":"https://avatars.githubusercontent.com/u/119974?v=3","gravatar_id":"","url":"https://api.github.com/users/coleifer","html_url":"https://github.com/coleifer","followers_url":"https://api.github.com/users/coleifer/followers","following_url":"https://api.github.com/users/coleifer/following{/other_user}","gists_url":"https://api.github.com/users/coleifer/gists{/gist_id}","starred_url":"https://api.github.com/users/coleifer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/coleifer/subscriptions","organizations_url":"https://api.github.com/users/coleifer/orgs","repos_url":"https://api.github.com/users/coleifer/repos","events_url":"https://api.github.com/users/coleifer/events{/privacy}","received_events_url":"https://api.github.com/users/coleifer/received_events","type":"User","site_admin":false},"created_at":"2015-11-30T18:58:20Z","updated_at":"2015-11-30T18:58:20Z","body":"Interestingly, I'm running into a little trouble trying to test a virtual table implementation of my own. I've verified that the appropriate methods on the actual implementation are being called by using `print` statements. No results are ever returned, though.\r\n\r\nHere is the wrapper class:\r\n\r\n```python\r\nclass TableFunction(object):\r\n    columns = None\r\n    parameters = None\r\n    name = None\r\n\r\n    @classmethod\r\n    def get_columns(cls):\r\n        if cls.columns is None:\r\n            raise ValueError('No columns defined.')\r\n        return cls.columns\r\n\r\n    @classmethod\r\n    def get_parameters(self):\r\n        if cls.parameters is None:\r\n            raise ValueError('No parameters defined.')\r\n        return cls.parameters\r\n\r\n    def initialize(self, **query):\r\n        raise NotImplementedError\r\n\r\n    def iterate(self, idx):\r\n        raise NotImplementedError\r\n\r\n    @classmethod\r\n    def module(cls):\r\n        class Cursor(object):\r\n            def __init__(self, table_func):\r\n                self.table_func = table_func\r\n                self.current_row = None\r\n                self.iterator = None\r\n                self._idx = 0\r\n                self._consumed = False\r\n\r\n            def Close(self):\r\n                pass\r\n\r\n            def Column(self, idx):\r\n                if not self.current_row:\r\n                    raise ValueError('No current row.')\r\n                if idx == -1:\r\n                    return self._idx\r\n                return self.current_row[idx - 1]\r\n\r\n            def Eof(self):\r\n                return self._consumed\r\n\r\n            def Filter(self, idx_num, idx_name, constraint_args):\r\n                query = {}\r\n                params = idx_name.split(',')\r\n                for idx, param in enumerate(params):\r\n                    value = constraint_args[idx]\r\n                    query[param] = value\r\n\r\n                self.table_func.initialize(**query)\r\n                self.Next()\r\n\r\n            def Next(self):\r\n                try:\r\n                    self.current_row = self.table_func.iterate(self._idx)\r\n                except StopIteration:\r\n                    self._consumed = True\r\n                    self.current_row = None\r\n                else:\r\n                    self._idx += 1\r\n\r\n            def Rowid(self):\r\n                return self._idx + 1\r\n\r\n        class Table(object):\r\n            def __init__(self, table_func, params):\r\n                self.table_func = table_func\r\n                self.params = params\r\n\r\n            def Open(self):\r\n                return Cursor(self.table_func())\r\n\r\n            def Disconnect(self):\r\n                pass\r\n\r\n            Destroy = Disconnect\r\n\r\n            def UpdateChangeRow(self, *args):\r\n                raise ValueError('Cannot modify eponymous virtual table.')\r\n\r\n            UpdateDeleteRow = UpdateInsertRow = UpdateChangeRow\r\n\r\n            def BestIndex(self, constraints, order_bys):\r\n                constraints_used = []\r\n                columns = []\r\n                for i, (column_idx, comparison) in enumerate(constraints):\r\n                    if comparison != apsw.SQLITE_INDEX_CONSTRAINT_EQ:\r\n                        continue\r\n\r\n                    constraints_used.append(i)\r\n                    columns.append(self.params[column_idx - 1])\r\n\r\n                return [\r\n                    constraints_used,\r\n                    0,\r\n                    ','.join(columns),\r\n                    False,\r\n                    1000,\r\n                ]\r\n\r\n        class BaseModule(object):\r\n            def Connect(self, connection, module_name, db_name, table_name,\r\n                        *args):\r\n                columns = cls.get_columns()\r\n                parameters = cls.get_parameters()\r\n\r\n                columns = ','.join(\r\n                    columns +\r\n                    ['%s HIDDEN' % param for param in parameters])\r\n\r\n                return 'CREATE TABLE x(%s)' % columns, Table(cls, parameters)\r\n\r\n            Create = Connect\r\n\r\n        module = type('%sModule' % cls.__name__, (BaseModule,), {})\r\n        return module()\r\n```\r\n\r\nTo test I've created a simple implementation that allows searching for regex results iteratively:\r\n\r\n```python\r\nclass ReSearch(TableFunction):\r\n    @classmethod\r\n    def get_columns(self):\r\n        return ['grp']\r\n\r\n    @classmethod\r\n    def get_parameters(self):\r\n        return ['regex', 'search_string']\r\n\r\n    def initialize(self, regex=None, search_string=None):\r\n        self._regex = regex\r\n        self._search_string = search_string\r\n        self._iter = re.finditer(self._regex, self._search_string)\r\n\r\n    def iterate(self, idx):\r\n        return [int(next(self._iter).group(0))]\r\n```\r\n\r\nThe following SQL should produce results, but does not for some reason:\r\n\r\n```sql\r\nCREATE VIRTUAL TABLE foo USING re_search();\r\nSELECT * FROM foo(?, ?) -- '[0-9]+', 'foo 003 test 302, bar 234 blah 1'\r\n```"}}