Changeset: 625c5d10d35a for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=625c5d10d35a
Added Files:
        tools/merovingian/daemon/snapshot.c
        tools/merovingian/daemon/snapshot.h
Modified Files:
        sql/jdbc/tests/Tests/JdbcClient_create_tables.sql
        sql/jdbc/tests/Tests/JdbcClient_inserts_selects.sql
        sql/storage/store.c
        sql/test/BugTracker-2019/Tests/next-get-value-bulk.Bug-6766.stable.out
        sql/test/Tests/50ways.sql
        sql/test/Tests/add_column_with_seq.sql
        sql/test/Tests/alter-sequence.sql
        sql/test/Tests/antiselect.sql
        sql/test/Tests/cast_str2num.sql
        sql/test/Tests/coalesce.sql
        sql/test/Tests/count.sql
        sql/test/Tests/crashme.sql
        sql/test/Tests/create_in_schema.sql
        sql/test/Tests/decimal.sql
        sql/test/Tests/decimal2.sql
        sql/test/Tests/default.sql
        sql/test/Tests/example-delete1.sql
        sql/test/Tests/example-delete2.sql
        sql/test/Tests/example-delete3.sql
        sql/test/Tests/example-drop.sql
        sql/test/Tests/example-queries.sql
        sql/test/Tests/example-rangeselect.sql
        sql/test/Tests/example-transaction.sql
        sql/test/Tests/example-update-view.sql
        sql/test/Tests/example-update1.sql
        sql/test/Tests/example-update2.sql
        sql/test/Tests/example-update3.sql
        sql/test/Tests/example.sql
        sql/test/Tests/function_syntax.sql
        sql/test/Tests/identifiers.sql
        sql/test/Tests/joins.sql
        sql/test/Tests/keys.sql
        sql/test/Tests/marcin1.sql
        sql/test/Tests/marcin2.sql
        sql/test/Tests/null.sql
        sql/test/Tests/order_by_simple_exp.sql
        sql/test/Tests/orderby_limit.sql
        sql/test/Tests/orderby_non_selection_column.sql
        sql/test/Tests/procedure_syntax.sql
        sql/test/Tests/psm.sql
        sql/test/Tests/restart-sequence.sql
        sql/test/Tests/round.sql
        sql/test/Tests/session_vars.sql
        sql/test/Tests/setoptimizer.sql
        sql/test/Tests/string.sql
        sql/test/Tests/table_function.sql
        sql/test/Tests/timezone.sql
        sql/test/Tests/zones2.sql
        sql/test/VOC/VOCcreate_user.sql
        sql/test/VOC/VOCdrop.sql
        sql/test/VOC/VOCdrop_user.sql
        sql/test/VOC/VOCinsert.sql
        sql/test/VOC/VOCmanual_examples.sql
        sql/test/VOC/VOCquery.sql
        sql/test/bugs/Tests/arith-bug-sf-949228.sql
        sql/test/bugs/Tests/autocommit_illegal_statements-bug-sf-953545.sql
        sql/test/bugs/Tests/case_cast-bug-sf-1019506.sql
        sql/test/bugs/Tests/case_parenthesis-bug-sf-1023056.sql
        sql/test/bugs/Tests/cast_bool2int-bug-sf-1000576.sql
        sql/test/bugs/Tests/cast_bool2int-bug-sf-951800.sql
        sql/test/bugs/Tests/cast_varchar2int-bug-sf-964165.sql
        sql/test/bugs/Tests/create_insert_select-bug-sf-900198.sql
        sql/test/bugs/Tests/create_insert_select_aggr-bug-00001.sql
        sql/test/bugs/Tests/date_parsing-bug-sf-960166.sql
        sql/test/bugs/Tests/drop_foreignkey_selfreferencing.sql
        sql/test/bugs/Tests/except-union-intersect-bug-sf-1146079.sql
        sql/test/bugs/Tests/extract_interval_bug-sf-1274088.sql
        sql/test/bugs/Tests/float_arith_cast2int-bug-sf-949255.sql
        sql/test/bugs/Tests/floor-bug-sf-967066.sql
        sql/test/bugs/Tests/foreignkey_leftjoin-bug-sf-854312.sql
        sql/test/bugs/Tests/groupby_having-bug-sf-947600.sql
        sql/test/bugs/Tests/groupby_having_charlength-bug-sf-943566.sql
        sql/test/bugs/Tests/innerjoin_between-bug-sf-960672.sql
        sql/test/bugs/Tests/innerjoin_multiple-bug-sf-943661.sql
        sql/test/bugs/Tests/insert_into_groupby-bug-sf-949135.sql
        sql/test/bugs/Tests/insert_into_select-bug-sf-978043.sql
        sql/test/bugs/Tests/insert_update_arith-bug-sf-900206.sql
        sql/test/bugs/Tests/interval_convert_bugs-sf-1274077-1274085.sql
        sql/test/bugs/Tests/interval_data_commutative_add_bug-sf-1263928.sql
        sql/test/bugs/Tests/interval_parse_bug-sf-1263967.sql
        sql/test/bugs/Tests/like-bug-sf-1031435.sql
        sql/test/bugs/Tests/like-bug-sf-1043521.sql
        sql/test/bugs/Tests/numeric-bug-sf-952758.sql
        sql/test/bugs/Tests/object_name-bug-sf-985242.sql
        sql/test/bugs/Tests/primary_key-bug-sf-975308.sql
        sql/test/bugs/Tests/savepoint_release-bug-sf-953560.sql
        sql/test/bugs/Tests/savepoint_rollback-bug-sf-1023652.sql
        sql/test/bugs/Tests/savepoint_rollback-bug-sf-953553.sql
        sql/test/bugs/Tests/select_constant_from_where_1=1-bug-sf-1019529.sql
        sql/test/bugs/Tests/select_orderby_alias-bug-sf-1024615.sql
        sql/test/bugs/Tests/selectlist_subquery-sf-1208599.sql
        sql/test/bugs/Tests/start_transaction-bug-sf-952755.sql
        sql/test/bugs/Tests/subselect_ambigious_columns-bug-sf-949071.sql
        sql/test/bugs/Tests/subselect_column-bug-sf-951206.sql
        
sql/test/bugs/Tests/subselect_multiple_unionall_where_1=1-bug-sf-1005596.sql
        sql/test/bugs/Tests/subselect_single_value-bug-sf-941791.sql
        sql/test/bugs/Tests/subselect_single_value-bug-sf-962099.sql
        sql/test/bugs/Tests/subselect_union-bug-sf-941826.sql
        sql/test/bugs/Tests/timestamp_comparison-bug-sf-1158581.sql
        sql/test/bugs/Tests/trimtest-bug-sf-1023588.sql
        sql/test/bugs/Tests/unicode_varchar-bug-sf-1041324.sql
        sql/test/bugs/Tests/union-bug-sf-978045.sql
        sql/test/bugs/Tests/union_all-bug-sf-941788.sql
        sql/test/bugs/Tests/union_all_where_1=1-bug-sf-936386.sql
        sql/test/bugs/Tests/unique-bug-sf-1043512.sql
        sql/test/bugs/Tests/update_where-bug-sf-976963.sql
        sql/test/bugs/Tests/view_join-bug-sf-990096.sql
        sql/test/bugs/Tests/view_selfjoin-bug-sf-937835.sql
        sql/test/miscellaneous/Tests/groupby_error.stable.out
        sql/test/testdb/Tests/dump-nogeom.sql
        sql/test/testdb/Tests/dump.sql
        sql/test/testdb/Tests/dump.timeout
        sql/test/testdb/Tests/load.timeout
        sql/test/testdb/Tests/package-hge.py
        sql/test/testdb/Tests/package.py
        sql/test/wlcr/Tests/wlr10.py
        tools/merovingian/client/monetdb.c
        tools/merovingian/daemon/Makefile.ag
        tools/merovingian/daemon/controlrunner.c
        tools/merovingian/daemon/merovingian.c
        tools/merovingian/utils/database.c
        tools/merovingian/utils/utils.c
        tools/merovingian/utils/utils.h
Branch: default
Log Message:

Merged hot-snapshot branch into default.


diffs (truncated from 1833 to 300 lines):

diff --git a/sql/storage/store.c b/sql/storage/store.c
--- a/sql/storage/store.c
+++ b/sql/storage/store.c
@@ -2554,7 +2554,11 @@ hot_snapshot_write_tar(stream *out, cons
                                goto end;
                }
        }
-       ret = GDK_SUCCEED;
+
+       // write a trailing block of zeros. If it succeeds, this function 
succeeds.
+       char a;
+       a = '\0';
+       ret = tar_write(out, &a, 1);
 
 end:
        free(plan);
@@ -2563,13 +2567,50 @@ end:
        return ret;
 }
 
+/* Pick a name for the temporary tar file. Make sure it has the same extension
+ * so as not to confuse the streams library.
+ *
+ * This function is not entirely safe as compare to for example mkstemp.
+ */
+static str pick_tmp_name(str filename)
+{
+       str name = GDKmalloc(strlen(filename) + 10);
+       if (name == NULL) {
+               GDKerror("malloc failed");
+               return NULL;
+       }
+       strcpy(name, filename);
+
+       // Look for an extension.
+       // Make sure it's part of the basename
+
+       char *ext = strrchr(name, '.');
+       char *sep = strrchr(name, DIR_SEP);
+       char *slash = strrchr(name, '/'); // on Windows, / and \ both work
+       if (ext != NULL && sep != NULL && sep > ext)
+               ext = NULL;
+       else if (ext != NULL && slash != NULL && slash > ext)
+               ext = NULL;
+
+       if (ext == NULL) {
+               return strcat(name, "..tmp");
+       } else {
+               char *tmp = "..tmp.";
+               size_t tmplen = strlen(tmp);
+               memmove(ext + tmplen, ext, strlen(ext) + 1);
+               memmove(ext, tmp, tmplen);
+       }
+
+       return name;
+}
+
 extern lng
 store_hot_snapshot(str tarfile)
 {
        int locked = 0;
        lng result = 0;
-       char tmppath[FILENAME_MAX];
-       char dirpath[FILENAME_MAX];
+       char *tmppath = NULL;
+       char *dirpath = NULL;
        int do_remove = 0;
        int dir_fd = -1;
        stream *tar_stream = NULL;
@@ -2582,7 +2623,10 @@ store_hot_snapshot(str tarfile)
                goto end;
        }
 
-       snprintf(tmppath, sizeof(tmppath), "%s.tmp", tarfile);
+       tmppath = pick_tmp_name(tarfile);
+       if (tmppath == NULL) {
+               goto end;
+       }
        tar_stream = open_wstream(tmppath);
        if (!tar_stream) {
                GDKerror("Failed to open %s for writing", tmppath);
@@ -2600,8 +2644,13 @@ store_hot_snapshot(str tarfile)
        // Call realpath(2) to make the path absolute so it has at least
        // one DIR_SEP in it. Realpath requires the file to exist so
        // we feed it tmppath rather than tarfile.
-       if (realpath(tmppath, dirpath) == NULL) { // ERROR no realpath
-               GDKsyserror("couldn't resolve path %s", tarfile);
+       dirpath = GDKmalloc(PATH_MAX);
+       if (dirpath == NULL) {
+               GDKsyserror("malloc failed");
+               goto end;
+       }
+       if (realpath(tmppath, dirpath) == NULL) {
+               GDKsyserror("couldn't resolve path %s: %s", tarfile, 
strerror(errno));
                goto end;
        }
        *strrchr(dirpath, DIR_SEP) = '\0';
@@ -2616,7 +2665,7 @@ store_hot_snapshot(str tarfile)
        }
 
        // Fsync the directory. Postgres believes this is necessary for 
durability.
-       if (fsync(dir_fd) < 0) { // ERROR no fsync
+       if (fsync(dir_fd) < 0) {
                GDKsyserror("First fsync on %s failed", dirpath);
                goto end;
        }
@@ -2678,6 +2727,8 @@ store_hot_snapshot(str tarfile)
        result = 42;
 
 end:
+       GDKfree(tmppath);
+       GDKfree(dirpath);
        if (dir_fd >= 0)
                close(dir_fd);
        if (locked)
diff --git a/tools/merovingian/client/monetdb.c 
b/tools/merovingian/client/monetdb.c
--- a/tools/merovingian/client/monetdb.c
+++ b/tools/merovingian/client/monetdb.c
@@ -58,10 +58,11 @@ command_help(int argc, char *argv[])
        if (argc < 2) {
                printf("Usage: monetdb [options] command 
[command-options-and-arguments]\n");
                printf("  where command is one of:\n");
-               printf("    create, destroy, lock, release\n");
-               printf("    status, start, stop, kill\n");
-               printf("    profilerstart, profilerstop\n");
-               printf("    set, get, inherit\n");
+               printf("    create, destroy, lock, release,\n");
+               printf("    status, start, stop, kill,\n");
+               printf("    profilerstart, profilerstop,\n");
+               printf("    snapshot,\n");
+               printf("    set, get, inherit,\n");
                printf("    discover, help, version\n");
                printf("  options can be:\n");
                printf("    -q       suppress status output\n");
@@ -169,6 +170,39 @@ command_help(int argc, char *argv[])
        } else if (strcmp(argv[1], "version") == 0) {
                printf("Usage: monetdb version\n");
                printf("  prints the version of this monetdb utility\n");
+       } else if (strcmp(argv[1], "snapshot") == 0) {
+               if (argc > 2 && strcmp(argv[2], "list") == 0) {
+                       printf("Usage: monetdb snapshot list [<dbname>...]\n");
+                       printf("  List snapshots for the given database, or all 
databases\n");
+                       printf("  if none given.\n");
+               } else if (argc > 2 && strcmp(argv[2], "create") == 0) {
+                       printf("Usage: monetdb snapshot create [-t 
<targetfile>] <dbname> [<dbname>..]\n");
+                       printf("  Take a snapshot of the listed databases. 
Unless -t is given, the snapshots\n");
+                       printf("  are written to files named\n");
+                       printf("  
<snapshotdir>/<dbname>_<YYYY><MM><DD>T<HH><MM>UTC.tar.gz.\n");
+                       printf("Options:\n");
+                       printf("  -t <targetfile>  File on the server to write 
the snapshot to.\n");
+               } else if (argc > 2 && strcmp(argv[2], "restore") == 0) {
+                       printf("Usage: monetdb snapshot restore [-f] <snapid> 
[dbname]\n");
+                       printf("  Create a database from the given snapshot, 
where  <snapid> is either\n");
+                       printf("  a path on the server or <dbname>@<num> as 
produced\n");
+                       printf("Options:\n");
+                       printf("  -f  do not ask for confirmation\n");
+               } else if (argc > 2 && strcmp(argv[2], "destroy") == 0) {
+                       printf("Usage: monetdb snapshot destroy [-f] 
<snapid>...\n");
+                       printf("       monetdb snapshot destroy [-f] -r <N> 
<dbname>...\n");
+                       printf("  Destroy one or more database snapshots, 
identified by a database name\n");
+                       printf("  and a sequence number as given by 'monetdb 
snapshot list'.\n");
+                       printf("  In the first form, the sequence numbers are 
part of the <snapid>.\n");
+                       printf("  In the second form. <dbname> is a database 
name or pattern such as 'staging*'\n");
+                       printf("  and N is the number of snapshots to 
retain.\n");
+                       printf("Options:\n");
+                       printf("  -f  Do not ask for confirmation\n");
+                       printf("  -r  Number of snapshots to retain.\n");
+               } else {
+                       printf("Usage: monetdb <create|list|restore|destroy> 
[arguments]\n");
+                       printf("  Manage database snapshots\n");
+               }
        } else {
                printf("help: unknown command: %s\n", argv[1]);
        }
@@ -1658,6 +1692,599 @@ command_profilerstop(int argc, char *arg
        simple_command(argc, argv, "profilerstop", "stopped profiler", 1);
 }
 
+/* Snapshot this single database to the given file */
+static void
+snapshot_create_adhoc(sabdb *databases, char *filename) {
+       /* databases is supposed to only hold a single database */
+       assert(databases != NULL);
+       assert(databases->next == NULL);
+
+       char *merocmd = malloc(100 + strlen(filename));
+       sprintf(merocmd, "snapshot create adhoc %s", filename);
+
+       simple_argv_cmd("snapshot", databases, merocmd, NULL, "snapshotting 
database");
+
+       free(merocmd);
+}
+
+/* Create automatic snapshots of the given databases */
+static void
+snapshot_create_automatic(sabdb *databases) {
+       simple_argv_cmd("snapshot", databases, "snapshot create automatic", 
NULL, "snapshotting database");
+}
+
+/* Comparison function used for qsort */
+static int
+snapshot_enumerate_helper(const void *left, const void *right)
+{
+       const struct snapshot *left_snap = left;
+       const struct snapshot *right_snap = right;
+       int cmp;
+
+       cmp = strcmp(left_snap->dbname, right_snap->dbname);
+       if (cmp != 0)
+               return cmp;
+
+       // Careful! Sort newest to oldest
+       if (left_snap->time < right_snap->time)
+               return +1; // !!
+       if (left_snap->time > right_snap->time)
+               return -1; // !!
+
+       // No preference
+       return 0;
+}
+
+/* Retrieve a list of all snapshots and Store it in the array. */
+static char*
+snapshot_enumerate(struct snapshot **snapshots, int *nsnapshots)
+{
+       int ninitial = *nsnapshots;
+       char *out = NULL;
+       char *ret = control_send(&out, mero_host, mero_port, "", "snapshot 
list", 1, mero_pass);
+       if (ret != NULL)
+               return ret;
+
+       if (strcmp(out, "OK1") == 0) {
+               // ok, empty resultset
+               free(out);
+       } else if (strncmp(out, "OK1\n", 4) == 0) {
+               // ok, nonempty resultset. Parse it.
+               char *p = out + 4;
+               char *end = p + strlen(p);
+               while (p < end) {
+                       char *eol = strchr(p, '\n');
+                       eol = (eol != NULL) ? eol : end;
+                       int64_t time;
+                       uint64_t size;
+                       int len;
+                       if (sscanf(p, "%" SCNd64 " %" SCNu64 " %n", &time, 
&size, &len) != 2) {
+                               free(out);
+                               return strdup("internal parse error");
+                       }
+                       p += len;
+                       char *dbend = strchr(p, ' ');
+                       if (dbend == NULL) {
+                               free(out);
+                               return strdup("internal parse error");
+                       }
+                       int dblen = dbend - p;
+                       char *path = dbend + 1;
+                       int pathlen = eol - path;
+                       struct snapshot *snap = push_snapshot(snapshots, 
nsnapshots);
+                       snap->dbname = malloc(dblen + 1);
+                       memmove(snap->dbname, p, dblen);
+                       snap->dbname[dblen] = '\0';
+                       snap->time = time;
+                       snap->size = size;
+                       snap->path = malloc(pathlen + 1);
+                       memmove(snap->path, path, pathlen);
+                       p = eol + 1;
+               };
+               free(out);
+       } else {
+               return out;
+       }
+
+       // Sort them and give names of the form dbname@seqno
+       if (*nsnapshots > ninitial) {
+               int sort_len = *nsnapshots - ninitial;
+               struct snapshot *sort_start = *snapshots + ninitial;
+               qsort(sort_start, sort_len, sizeof(struct snapshot), 
snapshot_enumerate_helper);
+               struct snapshot *prev = NULL;
+               int counter;
+               for (struct snapshot *cur = sort_start; cur < sort_start + 
sort_len; cur++) {
+                       if (prev == NULL || strcmp(prev->dbname, cur->dbname) 
!= 0)
+                               counter = 0;
+                       counter++;
+                       cur->name = malloc(strlen(cur->dbname) + 10);
+                       sprintf(cur->name, "%s@%d", cur->dbname, counter);
+                       prev = cur;
+               }
+       }
+
+       return NULL;
+}
+
+static void
+snapshot_list(int nglobs, char *globs[]) {
+       struct snapshot *snapshots = NULL;
+       int nsnapshots = 0;
+
+       // Retrieve the full snapshot list
+       char *err = snapshot_enumerate(&snapshots, &nsnapshots);
+       if (err != NULL) {
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to