Changeset: 5bea62ec9cdb for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=5bea62ec9cdb
Modified Files:
        sql/backends/monet5/sql.c
        sql/include/sql_catalog.h
        sql/server/rel_updates.c
        sql/server/sql_parser.y
        sql/server/sql_scan.c
Branch: copyfromremote
Log Message:

Implement COPY INTO ... FROM 'http://...'

Only if GRANT'ed COPY FROM REMOTE permission.
Adds a new non-reserved keyword FILE as the full name
of the COPY FROM permission is now COPY FROM FILE.

A bare COPY FROM permission is equivalent to COPY FROM FILE.

FROM 'https://' is also supported.
Have to check if and how certificates are checked.


diffs (210 lines):

diff --git a/sql/backends/monet5/sql.c b/sql/backends/monet5/sql.c
--- a/sql/backends/monet5/sql.c
+++ b/sql/backends/monet5/sql.c
@@ -3102,9 +3102,12 @@ mvc_import_table_wrap(Client cntxt, MalB
                                return msg;
                        }
                } else {
-                       ss = open_rastream(fname);
+                       if (strncmp("http:", fname, 5) == 0 || 
strncmp("https:", fname, 6) == 0)
+                               ss = open_urlstream(fname);
+                       else
+                               ss = open_rastream(fname);
                        if (ss == NULL || mnstr_errnr(ss)) {
-                               msg = createException(IO, "sql.copy_from", 
SQLSTATE(42000) "Cannot open file '%s': %s", fname, GDKstrerror(errno, 
(char[128]){0}, 128));
+                               msg = createException(IO, "sql.copy_from", 
SQLSTATE(42000) "Cannot open '%s': %s", fname, GDKstrerror(errno, 
(char[128]){0}, 128));
                                close_stream(ss);
                                return msg;
                        }
diff --git a/sql/include/sql_catalog.h b/sql/include/sql_catalog.h
--- a/sql/include/sql_catalog.h
+++ b/sql/include/sql_catalog.h
@@ -40,6 +40,7 @@
 /* global privs */
 #define PRIV_COPYFROMFILE 1
 #define PRIV_COPYINTOFILE 2
+#define PRIV_COPYFROMURL 3
 
 typedef enum sql_dependency { 
        SCHEMA_DEPENDENCY = 1,
diff --git a/sql/server/rel_updates.c b/sql/server/rel_updates.c
--- a/sql/server/rel_updates.c
+++ b/sql/server/rel_updates.c
@@ -416,12 +416,55 @@ insert_allowed(mvc *sql, sql_table *t, c
        return t;
 }
 
-static int 
-copy_allowed(mvc *sql, int from)
+static int
+copy_from_file_allowed(mvc *sql)
+{
+       return global_privs(sql, PRIV_COPYFROMFILE);
+}
+
+static int
+copy_into_file_allowed(mvc *sql)
+{
+       return global_privs(sql, PRIV_COPYINTOFILE);
+}
+
+static int
+copy_from_src_allowed(mvc *sql, int locked, int onclient, const char *src)
 {
-       if (!global_privs(sql, (from)?PRIV_COPYFROMFILE:PRIV_COPYINTOFILE)) 
+       // LOCKED requires admin privileges so it has to go through the decision
+       // procedure below. Apart from that, ON CLIENT is checked by the client 
so
+       // it is always ok.
+       if (onclient && !locked)
+               return 1; // let the client figure it out.
+
+       int is_url; // Intentionally left uninitialized.
+       if (src == NULL)
+               is_url = 0;
+       else if (strncmp("http:", src, 5) == 0)
+               is_url = 1;
+       else if (strncmp("https:", src, 6) == 0)
+               is_url = 1;
+       else if (MT_path_absolute(src))
+               is_url = 0;
+       else {
+               char *escaped = ATOMformat(TYPE_str, src);
+               sql_error(
+                       sql, 02,
+                       SQLSTATE(42000) "COPY INTO: filename must be absolute: 
%s",
+                       escaped);
+               GDKfree(escaped);
                return 0;
-       return 1;
+       }
+
+       int allowed = global_privs(sql, is_url ? PRIV_COPYFROMURL : 
PRIV_COPYFROMFILE);
+       if (!allowed) {
+               sql_error(sql, 02, SQLSTATE(42000)
+                                        "COPY INTO: insufficient privileges: "
+                                        "COPY INTO from file or url requires 
database administrator rights, "
+                                        "use 'COPY INTO ... FROM ... ON 
CLIENT' instead");
+       }
+
+       return allowed;
 }
 
 sql_table *
@@ -1623,10 +1666,6 @@ copyfrom(sql_query *query, dlist *qname,
                return NULL;
        /* Only the MONETDB user is allowed copy into with
           a lock and only on tables without idx */
-       if (locked && !copy_allowed(sql, 1)) {
-               return sql_error(sql, 02, SQLSTATE(42000) "COPY INTO: 
insufficient privileges: "
-                   "COPY INTO from .. LOCKED requires database administrator 
rights");
-       }
        if (locked && (!list_empty(t->idxs.set) || !list_empty(t->keys.set))) {
                return sql_error(sql, 02, SQLSTATE(42000) "COPY INTO: 
insufficient privileges: "
                    "COPY INTO from .. LOCKED requires tables without indices");
@@ -1698,24 +1737,12 @@ copyfrom(sql_query *query, dlist *qname,
        if (files) {
                dnode *n = files->h;
 
-               if (!onclient && !copy_allowed(sql, 1)) {
-                       return sql_error(sql, 02, SQLSTATE(42000)
-                                        "COPY INTO: insufficient privileges: "
-                                        "COPY INTO from file(s) requires 
database administrator rights, "
-                                        "use 'COPY INTO \"%s\" FROM file ON 
CLIENT' instead", tname);
-               }
-
                for (; n; n = n->next) {
                        const char *fname = n->data.sval;
                        sql_rel *nrel;
 
-                       if (!onclient && fname && !MT_path_absolute(fname)) {
-                               char *fn = ATOMformat(TYPE_str, fname);
-                               sql_error(sql, 02, SQLSTATE(42000) "COPY INTO: 
filename must "
-                                         "have absolute path: %s", fn);
-                               GDKfree(fn);
+                       if (!copy_from_src_allowed(sql, locked, onclient, 
fname))
                                return NULL;
-                       }
 
                        nrel = rel_import(sql, nt, tsep, rsep, ssep, ns, fname, 
nr, offset, locked, best_effort, fwf_widths, onclient);
 
@@ -1730,6 +1757,8 @@ copyfrom(sql_query *query, dlist *qname,
                }
        } else {
                assert(onclient == 0);
+               if (!copy_from_src_allowed(sql, locked, onclient, NULL))
+                       return NULL;
                rel = rel_import(sql, nt, tsep, rsep, ssep, ns, NULL, nr, 
offset, locked, best_effort, NULL, onclient);
        }
        if (headers) {
@@ -1818,7 +1847,7 @@ bincopyfrom(sql_query *query, dlist *qna
        int i;
 
        assert(f);
-       if (!copy_allowed(sql, 1))
+       if (!copy_from_file_allowed(sql))
                return sql_error(sql, 02, SQLSTATE(42000) "COPY INTO: 
insufficient privileges: "
                                "binary COPY INTO requires database 
administrator rights");
 
@@ -1893,7 +1922,7 @@ copyfromloader(sql_query *query, dlist *
        sql_rel* rel = NULL;
        sql_table* t;
 
-       if (!copy_allowed(sql, 1))
+       if (!copy_from_file_allowed(sql))
                return sql_error(sql, 02, SQLSTATE(42000) "COPY INTO: 
insufficient privileges: "
                                "binary COPY INTO requires database 
administrator rights");
        if (sname && !(s = mvc_bind_schema(sql, sname)))
@@ -1978,7 +2007,7 @@ copyto(sql_query *query, symbol *sq, con
 
        if (!onclient && filename) {
                struct stat fs;
-               if (!copy_allowed(sql, 0))
+               if (!copy_into_file_allowed(sql))
                        return sql_error(sql, 02, SQLSTATE(42000) "COPY INTO: 
insufficient privileges: "
                                         "COPY INTO file requires database 
administrator rights, "
                                         "use 'COPY ... INTO file ON CLIENT' 
instead");
diff --git a/sql/server/sql_parser.y b/sql/server/sql_parser.y
--- a/sql/server/sql_parser.y
+++ b/sql/server/sql_parser.y
@@ -702,7 +702,7 @@ SQLCODE SQLERROR UNDER WHENEVER
 
 %token CASE WHEN THEN ELSE NULLIF COALESCE IF ELSEIF WHILE DO
 %token ATOMIC BEGIN END
-%token COPY RECORDS DELIMITERS STDIN STDOUT FWF CLIENT SERVER
+%token COPY sqlFILE RECORDS DELIMITERS STDIN STDOUT FWF CLIENT SERVER
 %token INDEX REPLACE
 
 %token AS TRIGGER OF BEFORE AFTER ROW STATEMENT sqlNEW OLD EACH REFERENCING
@@ -1092,8 +1092,11 @@ global_privileges:
  ;
 
 global_privilege:
-       COPY FROM       { $$ = PRIV_COPYFROMFILE; }
- |     COPY INTO       { $$ = PRIV_COPYINTOFILE; }
+       COPY FROM               { $$ = PRIV_COPYFROMFILE; }
+ |     COPY FROM sqlFILE       { $$ = PRIV_COPYFROMFILE; }
+ |     COPY FROM REMOTE        { $$ = PRIV_COPYFROMURL; }
+ |     COPY INTO               { $$ = PRIV_COPYINTOFILE; }
+ |     COPY INTO sqlFILE       { $$ = PRIV_COPYINTOFILE; }
  ;
 
 object_name:
@@ -5596,6 +5599,7 @@ non_reserved_word:
 | DIAGNOSTICS  { $$ = sa_strdup(SA, "diagnostics"); }
 | SQL_EXPLAIN  { $$ = sa_strdup(SA, "explain"); }
 | FIRST                { $$ = sa_strdup(SA, "first"); }
+| sqlFILE              { $$ = sa_strdup(SA, "file"); }
 | GEOMETRY     { $$ = sa_strdup(SA, "geometry"); }
 | IMPRINTS     { $$ = sa_strdup(SA, "imprints"); }
 | INCREMENT    { $$ = sa_strdup(SA, "increment"); }
diff --git a/sql/server/sql_scan.c b/sql/server/sql_scan.c
--- a/sql/server/sql_scan.c
+++ b/sql/server/sql_scan.c
@@ -143,6 +143,7 @@ scanner_init_keywords(void)
        failed += keywords_insert("CREATE", CREATE);
        failed += keywords_insert("CROSS", CROSS);
        failed += keywords_insert("COPY", COPY);
+       failed += keywords_insert("FILE", sqlFILE);
        failed += keywords_insert("RECORDS", RECORDS);
        failed += keywords_insert("DELIMITERS", DELIMITERS);
        failed += keywords_insert("STDIN", STDIN);
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to