Repository: biolab/orange3

Issue:
sample_time requires installation of extension in orange3/Orange/data/sql/table.py
<!--
This is an issue template. Please fill in the relevant details in the
sections below.
-->
##### Orange version

<!-- From menu _Help→About→Version_ or code `Orange.version.full_version` -->

3.3.5
##### Expected behavior

Sampling of large table for attribute discovery
![screen shot 2016-06-29 at 08 48 43](https://cloud.githubusercontent.com/assets/13068310/16454569/ddc2554a-3dd6-11e6-9c52-6bce59226f44.png)
##### Actual behavior

Error that method system_time doesn't exist
##### Steps to reproduce the behavior

Connect to postgres database, Answer "Yes, on a sample" to the popup box.

![screen shot 2016-06-29 at 08 48 28](https://cloud.githubusercontent.com/assets/13068310/16454548/cce1922c-3dd6-11e6-84b8-3e136ae13cd9.png)
##### Additional info (worksheets, data, screenshots, ...)

The solution is to [create the system_time extension](https://www.postgresql.org/docs/9.5/static/tsm-system-time.html) in Postgres 9.5+ (not available before then).

Suggested solution is to check for Postgres 9.5+ and the existence of the system_time method. If it doesn't exist, create the method using the following:

`CREATE EXTENSION tsm_system_time;`

After installing the extension, the sampling works properly.



Discussion:
@lanzagar Could you please check? :rose: 

Nothing to check really, this is not unexpected behaviour, but how it works for now.
Having the extension is a requirement for using (larger) SQL data.
We could try and create the extension automatically, but it will still work only in some cases. The extension might not be available on the server at all (needs to be installed first), or the user could have only read and no write permissions.

But it would not hurt to try and run the CREATE EXTENSION command in case it can be done.
Will add this as an enhancement request on the TODO list.


Resolving patch:
diff --git a/Orange/widgets/data/owsql.py b/Orange/widgets/data/owsql.py
--- a/Orange/widgets/data/owsql.py
+++ b/Orange/widgets/data/owsql.py
@@ -15,6 +15,7 @@
 
 
 MAX_DL_LIMIT = 1000000
+EXTENSIONS = ('tsm_system_time', 'quantile')
 
 
 class OWSql(widget.OWWidget):
@@ -132,20 +133,20 @@ def __init__(self):
 
     def error(self, id=0, text=""):
         super().error(id, text)
+        err_style = 'QLineEdit {border: 2px solid red;}'
         if 'server' in text or 'host' in text:
-            self.servertext.setStyleSheet('QLineEdit {border: 2px solid red;}')
+            self.servertext.setStyleSheet(err_style)
         else:
             self.servertext.setStyleSheet('')
         if 'role' in text:
-            self.usernametext.setStyleSheet('QLineEdit {border: 2px solid red;}')
+            self.usernametext.setStyleSheet(err_style)
         else:
             self.usernametext.setStyleSheet('')
         if 'database' in text:
-            self.databasetext.setStyleSheet('QLineEdit {border: 2px solid red;}')
+            self.databasetext.setStyleSheet(err_style)
         else:
             self.databasetext.setStyleSheet('')
 
-
     def connect(self):
         hostport = self.servertext.text().split(':')
         self.host = hostport[0]
@@ -173,9 +174,9 @@ def connect(self):
             self.database_desc = self.data_desc_table = None
             self.tablecombo.clear()
 
-
     def refresh_tables(self):
         self.tablecombo.clear()
+        self.error(1)
         if self._connection is None:
             self.data_desc_table = None
             return
@@ -215,7 +216,25 @@ def select_table(self):
             self.database_desc["Table"] = "(None)"
             self.table = None
 
+    def create_extensions(self):
+        missing = []
+        for ext in EXTENSIONS:
+            try:
+                cur = self._connection.cursor()
+                cur.execute("CREATE EXTENSION IF NOT EXISTS " + ext)
+            except psycopg2.OperationalError:
+                missing.append(ext)
+            finally:
+                self._connection.commit()
+        if missing:
+            self.error(1, 'Database missing extension{}: {}'.format(
+                's' if len(missing) > 1 else '',
+                ', '.join(missing)))
+        else:
+            self.error(1)
+
     def open_table(self):
+        self.create_extensions()
         table = self.get_table()
         self.data_desc_table = table
         self.send("Data", table)


Test patch: