From fe37f15577631ec700db3d958c395bb00f6f7e97 Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Tue, 30 Jul 2024 19:38:58 +0200
Subject: [PATCH] More hotfix data ingest

---
 .../java/at/tuwien/mapper/DataMapper.java     |  47 +-------
 .../java/at/tuwien/mapper/MariaDbMapper.java  |  30 -----
 dbrepo-metadata-db/1_setup-schema.sql         |   3 +-
 dbrepo-ui/components/table/TableImport.vue    | 108 +++++++++++-------
 dbrepo-ui/components/table/TableSchema.vue    |  16 ++-
 dbrepo-ui/locales/de-AT.json                  |   6 +-
 dbrepo-ui/locales/en-US.json                  |  11 +-
 dbrepo-ui/nuxt.config.ts                      |   6 +-
 .../pages/database/[database_id]/settings.vue |   7 +-
 .../[database_id]/table/[table_id]/import.vue |   1 +
 .../[database_id]/table/create/dataset.vue    |   8 +-
 11 files changed, 112 insertions(+), 131 deletions(-)

diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java
index 2e95fc34d2..796ef3edeb 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/DataMapper.java
@@ -553,7 +553,6 @@ public interface DataMapper {
         }
         /* boolean encoding fix */
         if (column.getColumnType().equals(ColumnTypeDto.TINYINT) && column.getSize() == 1) {
-            log.trace("column {} is of type tinyint with size {}: map to boolean", column.getInternalName(), column.getSize());
             column.setColumnType(ColumnTypeDto.BOOL);
         }
         switch (column.getColumnType()) {
@@ -562,10 +561,9 @@ public interface DataMapper {
                     log.error("Missing date format for column {}", column.getId());
                     throw new IllegalArgumentException("Missing date format");
                 }
-                log.trace("mapping {} to date with format '{}'", data, column.getDateFormat());
                 final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                         .parseCaseInsensitive() /* case insensitive to parse JAN and FEB */
-                        .appendPattern(column.getDateFormat().getUnixFormat())
+                        .appendPattern("yyyy-MM-dd")
                         .toFormatter(Locale.ENGLISH);
                 final LocalDate date = LocalDate.parse(String.valueOf(data), formatter);
                 return date.atStartOfDay(ZoneId.of("UTC"))
@@ -576,41 +574,32 @@ public interface DataMapper {
                     log.error("Missing date format for column {}", column.getId());
                     throw new IllegalArgumentException("Missing date format");
                 }
-                log.trace("mapping {} to timestamp with format '{}'", data, column.getDateFormat());
                 return Timestamp.valueOf(data.toString())
                         .toInstant();
             }
             case BINARY, VARBINARY, BIT -> {
-                log.trace("mapping {} -> binary", data);
                 return Long.parseLong(String.valueOf(data), 2);
             }
             case TEXT, CHAR, VARCHAR, TINYTEXT, MEDIUMTEXT, LONGTEXT, ENUM, SET -> {
-                log.trace("mapping {} -> string", data);
                 return String.valueOf(data);
             }
             case BIGINT -> {
-                log.trace("mapping {} -> biginteger", data);
                 return new BigInteger(String.valueOf(data));
             }
             case INT, SMALLINT, MEDIUMINT, TINYINT -> {
-                log.trace("mapping {} -> integer", data);
                 return Integer.parseInt(String.valueOf(data));
             }
             case DECIMAL, FLOAT, DOUBLE -> {
-                log.trace("mapping {} -> double", data);
                 return Double.valueOf(String.valueOf(data));
             }
             case BOOL -> {
-                log.trace("mapping {} -> boolean", data);
                 return Boolean.valueOf(String.valueOf(data));
             }
             case TIME -> {
-                log.trace("mapping {} -> time", data);
                 return String.valueOf(data);
             }
             case YEAR -> {
                 final String date = String.valueOf(data);
-                log.trace("mapping {} -> year", date);
                 return Short.valueOf(date.substring(0, date.indexOf('-')));
             }
         }
@@ -641,9 +630,6 @@ public interface DataMapper {
                     continue;
                 }
                 final Object object = dataColumnToObject(result.getObject(idx[0]++), column);
-                if (object == null) {
-                    log.warn("result set for column {} is empty (=null)", column.getInternalName());
-                }
                 map.put(columnOrAlias, object);
             }
             resultList.add(map);
@@ -664,7 +650,6 @@ public interface DataMapper {
     default void prepareStatementWithColumnTypeObject(PreparedStatement ps, ColumnTypeDto columnType, int idx, Object value) throws SQLException {
         switch (columnType) {
             case BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB:
-                log.trace("prepare statement idx {} blob", idx);
                 if (value == null) {
                     ps.setNull(idx, Types.BLOB);
                     break;
@@ -677,135 +662,105 @@ public interface DataMapper {
                 }
                 break;
             case TEXT, CHAR, VARCHAR, TINYTEXT, MEDIUMTEXT, LONGTEXT, ENUM, SET:
-                log.trace("prepare statement idx {} {} {}", idx, columnType, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.VARCHAR);
                     break;
                 }
                 ps.setString(idx, String.valueOf(value));
                 break;
             case DATE:
-                log.trace("prepare statement idx {} date {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.DATE);
                     break;
                 }
                 ps.setDate(idx, Date.valueOf(String.valueOf(value)));
                 break;
             case BIGINT:
-                log.trace("prepare statement idx {} bigint {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.BIGINT);
                     break;
                 }
                 ps.setLong(idx, Long.parseLong(String.valueOf(value)));
                 break;
             case INT, MEDIUMINT:
-                log.trace("prepare statement idx {} {} {}", idx, columnType, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.INTEGER);
                     break;
                 }
                 ps.setLong(idx, Long.parseLong(String.valueOf(value)));
                 break;
             case TINYINT:
-                log.trace("prepare statement idx {} tinyint {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.TINYINT);
                     break;
                 }
                 ps.setLong(idx, Long.parseLong(String.valueOf(value)));
                 break;
             case SMALLINT:
-                log.trace("prepare statement idx {} smallint {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.SMALLINT);
                     break;
                 }
                 ps.setLong(idx, Long.parseLong(String.valueOf(value)));
                 break;
             case DECIMAL:
-                log.trace("prepare statement idx {} decimal {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.DECIMAL);
                     break;
                 }
                 ps.setDouble(idx, Double.parseDouble(String.valueOf(value)));
                 break;
             case FLOAT:
-                log.trace("prepare statement idx {} float {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.FLOAT);
                     break;
                 }
                 ps.setDouble(idx, Double.parseDouble(String.valueOf(value)));
                 break;
             case DOUBLE:
-                log.trace("prepare statement idx {} double {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.DOUBLE);
                     break;
                 }
                 ps.setDouble(idx, Double.parseDouble(String.valueOf(value)));
                 break;
             case BINARY, VARBINARY, BIT:
-                log.trace("prepare statement idx {} {} {}", idx, columnType, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.DECIMAL);
                     break;
                 }
                 ps.setBinaryStream(idx, (InputStream) value);
                 break;
             case BOOL:
-                log.trace("prepare statement idx {} boolean {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.BOOLEAN);
                     break;
                 }
                 ps.setBoolean(idx, Boolean.parseBoolean(String.valueOf(value)));
                 break;
             case TIMESTAMP:
-                log.trace("prepare statement idx {} timestamp {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.TIMESTAMP);
                     break;
                 }
                 ps.setTimestamp(idx, Timestamp.valueOf(String.valueOf(value)));
                 break;
             case DATETIME:
-                log.trace("prepare statement idx {} datetime {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.TIMESTAMP);
                     break;
                 }
                 ps.setTimestamp(idx, Timestamp.valueOf(String.valueOf(value)));
                 break;
             case TIME:
-                log.trace("prepare statement idx {} time {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.TIME);
                     break;
                 }
                 ps.setTime(idx, Time.valueOf(String.valueOf(value)));
                 break;
             case YEAR:
-                log.trace("prepare statement idx {} year {}", idx, value);
                 if (value == null) {
-                    log.trace("idx {} is null, prepare with null value", idx);
                     ps.setNull(idx, Types.TIME);
                     break;
                 }
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java
index b2ed933049..e22f3025d1 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/mapper/MariaDbMapper.java
@@ -753,7 +753,6 @@ public interface MariaDbMapper {
         switch (columnType) {
             case BLOB, TINYBLOB, MEDIUMBLOB, LONGBLOB:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.BLOB);
                     break;
                 }
@@ -762,7 +761,6 @@ public interface MariaDbMapper {
                     try (ObjectOutputStream ois = new ObjectOutputStream(boas)) {
                         ois.writeObject(value);
                         statement.setBlob(idx, new ByteArrayInputStream(boas.toByteArray()));
-                        log.trace("prepare statement idx {} = {} blob", idx, columnName);
                     }
 
                 } catch (IOException e) {
@@ -772,128 +770,100 @@ public interface MariaDbMapper {
                 break;
             case TEXT, CHAR, VARCHAR, TINYTEXT, MEDIUMTEXT, LONGTEXT, ENUM, SET:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.VARCHAR);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} text/char/varchar/tinytext/mediumtext/longtext/enum/set: {}", idx, columnName, value);
                 statement.setString(idx, String.valueOf(value));
                 break;
             case DATE:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.DATE);
                     break;
                 }
-                log.trace("prepare statement idx {} date: {}", idx, value);
                 statement.setDate(idx, Date.valueOf(String.valueOf(value)));
                 break;
             case BIGINT:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.BIGINT);
                     break;
                 }
-                log.trace("prepare statement idx {} bigint: {}", idx, value);
                 statement.setLong(idx, Long.parseLong(String.valueOf(value)));
                 break;
             case INT, MEDIUMINT:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.INTEGER);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} int/mediumint: {}", idx, columnName, value);
                 statement.setLong(idx, Long.parseLong(String.valueOf(value)));
                 break;
             case TINYINT:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.TINYINT);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} tinyint: {}", idx, columnName, value);
                 statement.setLong(idx, Long.parseLong(String.valueOf(value)));
                 break;
             case SMALLINT:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.SMALLINT);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} smallint: {}", idx, columnName, value);
                 statement.setLong(idx, Long.parseLong(String.valueOf(value)));
                 break;
             case DECIMAL:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.DECIMAL);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} decimal: {}", idx, columnName, value);
                 statement.setDouble(idx, Double.parseDouble(String.valueOf(value)));
                 break;
             case FLOAT:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.FLOAT);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} float: {}", idx, columnName, value);
                 statement.setDouble(idx, Double.parseDouble(String.valueOf(value)));
                 break;
             case DOUBLE:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.DOUBLE);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} double: {}", idx, columnName, value);
                 statement.setDouble(idx, Double.parseDouble(String.valueOf(value)));
                 break;
             case BINARY, VARBINARY, BIT:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.DECIMAL);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} binary/varbinary/bit", idx, columnName);
                 statement.setBinaryStream(idx, (InputStream) value);
                 break;
             case BOOL:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.BOOLEAN);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} bool: {}", idx, columnName, value);
                 statement.setBoolean(idx, Boolean.parseBoolean(String.valueOf(value)));
                 break;
             case TIMESTAMP, DATETIME:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.TIMESTAMP);
                     break;
                 }
-                log.trace("prepare statement idx {} timestamp/datetime: {}", idx, value);
                 statement.setTimestamp(idx, Timestamp.valueOf(String.valueOf(value)));
                 break;
             case TIME:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.TIME);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} time: {}", idx, columnName, value);
                 statement.setTime(idx, Time.valueOf(String.valueOf(value)));
                 break;
             case YEAR:
                 if (value == null) {
-                    log.trace("idx {} = {} is null, prepare with null value", idx, columnName);
                     statement.setNull(idx, Types.TIME);
                     break;
                 }
-                log.trace("prepare statement idx {} = {} year: {}", idx, columnName, value);
                 statement.setString(idx, String.valueOf(value));
                 break;
             default:
diff --git a/dbrepo-metadata-db/1_setup-schema.sql b/dbrepo-metadata-db/1_setup-schema.sql
index e385ea4d9b..62dc5c3095 100644
--- a/dbrepo-metadata-db/1_setup-schema.sql
+++ b/dbrepo-metadata-db/1_setup-schema.sql
@@ -539,7 +539,8 @@ INSERT INTO `mdb_images_date` (iid, database_format, unix_format, example, has_t
 VALUES (1, '%Y-%c-%d %H:%i:%S.%f', 'yyyy-MM-dd HH:mm:ss.SSSSSS', '2022-01-30 13:44:25.499', true),
        (1, '%Y-%c-%d %H:%i:%S', 'yyyy-MM-dd HH:mm:ss', '2022-01-30 13:44:25', true),
        (1, '%Y-%c-%d', 'yyyy-MM-dd', '2022-01-30', false),
-       (1, '%H:%i:%S', 'HH:mm:ss', '13:44:25', true);
+       (1, '%H:%i:%S', 'HH:mm:ss', '13:44:25', true),
+       (1, '%d.%c.%Y', 'dd.MM.yyyy', '30.01.2022', false);
 
 INSERT INTO `mdb_ontologies` (prefix, uri, uri_pattern, sparql_endpoint, rdf_path)
 VALUES ('om', 'http://www.ontology-of-units-of-measure.org/resource/om-2/',
diff --git a/dbrepo-ui/components/table/TableImport.vue b/dbrepo-ui/components/table/TableImport.vue
index 22f7081751..65fdd4930d 100644
--- a/dbrepo-ui/components/table/TableImport.vue
+++ b/dbrepo-ui/components/table/TableImport.vue
@@ -142,7 +142,6 @@
           </v-col>
         </v-row>
         <v-form
-          v-if="!$route.query.location"
           ref="form"
           v-model="validStep2"
           :disabled="disabled"
@@ -213,6 +212,7 @@
               <v-col
                 cols="8">
                 <v-btn
+                  v-if="create && !$route.query.location"
                   :disabled="!isAnalyseAllowed || !validStep1 || !validStep2 || disabled"
                   :loading="loading"
                   :variant="buttonVariant"
@@ -220,45 +220,38 @@
                   size="small"
                   :text="$t('pages.table.subpages.import.analyse.text')"
                   @click="uploadAndAnalyse"/>
+                <v-btn
+                  v-if="!create && !$route.query.location"
+                  :disabled="!isAnalyseAllowed || !validStep1 || !validStep2 || disabled"
+                  :loading="loading || loadingImport"
+                  :variant="buttonVariant"
+                  color="secondary"
+                  size="small"
+                  :text="$t('pages.table.subpages.import.upload.text')"
+                  @click="uploadAndImport"/>
+                <v-btn
+                  v-if="!create && $route.query.location"
+                  :disabled="step > 2 || disabled"
+                  :loading="loading || loadingImport"
+                  :variant="buttonVariant"
+                  color="secondary"
+                  size="small"
+                  class="mt-2"
+                  :text="$t('pages.table.subpages.import.text')"
+                  @click="importCsv"/>
               </v-col>
             </v-row>
           </v-form>
         </v-container>
     </v-stepper-window>
-    <v-stepper-header
-      v-if="!create">
-      <v-stepper-item
-        :title="$t('pages.table.subpages.import.dataset.title')"
-        :complete="validStep3"
-        :value="3" />
-    </v-stepper-header>
-    <v-stepper-window
-      v-if="!create"
-      direction="vertical">
-      <v-container>
-        <v-row
-          dense>
-          <v-col>
-            <v-btn
-              color="secondary"
-              :disabled="step !== 3 || disabled"
-              size="small"
-              variant="flat"
-              :loading="loadingImport"
-              :text="$t('navigation.import')"
-              @click="importCsv"/>
-          </v-col>
-        </v-row>
-      </v-container>
-    </v-stepper-window>
     <v-stepper-header
       v-if="!create">
       <v-stepper-item
         :title="$t('pages.table.subpages.import.summary.title')"
-        :value="4"/>
+        :value="3"/>
     </v-stepper-header>
     <v-stepper-window
-      v-if="!create && step === 4"
+      v-if="!create && step === 3"
       direction="vertical">
       <v-container>
         <v-row
@@ -280,7 +273,7 @@
             <v-btn
               v-if="rowCount !== null"
               color="secondary"
-              :disabled="step !== 4 || disabled"
+              :disabled="step !== 3 || disabled"
               size="small"
               variant="flat"
               :text="$t('navigation.data')"
@@ -364,7 +357,7 @@ export default {
     this.setQueryParamSafely('line_termination')
     this.setQueryParamSafely('skip_lines')
     if (this.$route.query.location) {
-      this.step = 3
+      this.step = 2
       this.validStep2 = true
     }
   },
@@ -458,7 +451,7 @@ export default {
             .then((rowCount) => {
               this.rowCount = rowCount
             })
-          this.step = 4
+          this.step = 3
           this.validStep3 = true
           this.loadingImport = false
         })
@@ -473,22 +466,39 @@ export default {
         })
     },
     uploadAndAnalyse() {
-      this.loading = true
-      console.debug('upload file', this.file)
-      const uploadService = useUploadService()
-      return uploadService.create(this.file)
+      this.upload()
         .then((s3key) => {
-          const toast = useToastInstance()
-          toast.success(this.$t('success.upload.dataset'))
           this.analyse(s3key)
         })
-        .catch((error) => {
-          console.error('Failed to upload dataset', error)
-          const toast = useToastInstance()
-          toast.error(this.$t('error.upload.dataset'))
-          this.loading = false
+    },
+    uploadAndImport() {
+      this.upload()
+        .then((s3key) => {
+          this.tableImport.location = s3key
+          this.importCsv()
         })
     },
+    upload() {
+      this.loading = true
+      console.debug('upload file', this.file)
+      const uploadService = useUploadService()
+      return new Promise((resolve, reject) => {
+        return uploadService.create(this.file)
+          .then((s3key) => {
+            const toast = useToastInstance()
+            toast.success(this.$t('success.upload.dataset'))
+            this.loading = false
+            resolve(s3key)
+          })
+          .catch((error) => {
+            console.error('Failed to upload dataset', error)
+            const toast = useToastInstance()
+            toast.error(this.$t('error.upload.dataset'))
+            this.loading = false
+            reject(error)
+          })
+      })
+    },
     analyse(filename) {
       const analyseService = useAnalyseService()
       const payload = { filename }
@@ -520,7 +530,17 @@ export default {
           this.step = 3
           const toast = useToastInstance()
           toast.success(this.$t('success.analyse.dataset'))
-          this.$emit('analyse', {columns: this.columns, filename, line_termination})
+          this.$emit('analyse', {
+            columns: this.columns,
+            filename,
+            line_termination,
+            separator: this.tableImport.separator,
+            skip_lines: this.tableImport.skip_lines,
+            quote: this.tableImport.quote,
+            null_element: this.tableImport.null_element,
+            true_element: this.tableImport.true_element,
+            false_element: this.tableImport.false_element
+          })
           this.loading = false
         })
         .catch(({code, message}) => {
diff --git a/dbrepo-ui/components/table/TableSchema.vue b/dbrepo-ui/components/table/TableSchema.vue
index 51f186dd4a..e820d0aea2 100644
--- a/dbrepo-ui/components/table/TableSchema.vue
+++ b/dbrepo-ui/components/table/TableSchema.vue
@@ -147,7 +147,8 @@
             @click="removeColumn(idx)" />
         </v-col>
       </v-row>
-      <v-row dense>
+      <v-row
+        dense>
         <v-col>
           <v-btn
             size="small"
@@ -158,6 +159,16 @@
             @click="addColumn()" />
         </v-col>
       </v-row>
+      <v-row
+        v-if="showPrimaryKeyWarning">
+        <v-col md="8">
+          <v-alert
+            border="start"
+            color="warning">
+            {{ $t('pages.table.subpages.import.schema.primary.warn') }}
+          </v-alert>
+        </v-col>
+      </v-row>
       <v-row>
         <v-col>
           <v-btn
@@ -232,6 +243,9 @@ export default {
     buttonVariant () {
       const runtimeConfig = useRuntimeConfig()
       return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal
+    },
+    showPrimaryKeyWarning () {
+      return this.columns.filter(c => c.primary_key).length === 0
     }
   },
   watch: {
diff --git a/dbrepo-ui/locales/de-AT.json b/dbrepo-ui/locales/de-AT.json
index 4d9e25b36e..3cf6c0dfc1 100644
--- a/dbrepo-ui/locales/de-AT.json
+++ b/dbrepo-ui/locales/de-AT.json
@@ -324,6 +324,7 @@
       "subpages": {
         "import": {
           "title": "Erstellen Sie eine Tabelle aus einem CSV-/TSV-Datensatz",
+          "text": "Importieren",
           "metadata": {
             "title": "Tabellenmetadaten"
           },
@@ -404,6 +405,9 @@
           },
           "analyse": {
             "text": "Hochladen und analysieren"
+          },
+          "upload": {
+            "text": "Hochladen und importieren"
           }
         },
         "create": {
@@ -571,7 +575,7 @@
     "database": {
       "title": "Datenbank",
       "image": {
-        "title": "Teaser-Bild",
+        "title": "Datenbankbild",
         "alt": "Datenbanklogo/Standardbild"
       },
       "name": {
diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json
index 935023300e..cf8dcfd03e 100644
--- a/dbrepo-ui/locales/en-US.json
+++ b/dbrepo-ui/locales/en-US.json
@@ -324,12 +324,16 @@
       "subpages": {
         "import": {
           "title": "Create table from .csv/.tsv dataset",
+          "text": "Import",
           "metadata": {
             "title": "Table Metadata"
           },
           "schema": {
             "title": "Dataset Structure",
-            "text": "the table schema manually."
+            "text": "the table schema manually.",
+            "primary": {
+              "warn": "No primary key column(s) selected. Please select a column that uniquely identifies data entries."
+            }
           },
           "dataset": {
             "title": "Import Dataset",
@@ -404,6 +408,9 @@
           },
           "analyse": {
             "text": "Upload & Analyse"
+          },
+          "upload": {
+            "text": "Upload & Import"
           }
         },
         "create": {
@@ -571,7 +578,7 @@
     "database": {
       "title": "Database",
       "image": {
-        "title": "Teaser Image",
+        "title": "Database Image",
         "alt": "Database logo/default image"
       },
       "name": {
diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts
index 14a6c3034d..454cadf909 100644
--- a/dbrepo-ui/nuxt.config.ts
+++ b/dbrepo-ui/nuxt.config.ts
@@ -85,8 +85,8 @@ export default defineNuxtConfig({
      database: {
        unsupported: '*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--',
        image: {
-         width: 400,
-         height: 400
+         width: 200,
+         height: 200
        },
        extra: ''
      },
@@ -167,4 +167,4 @@ export default defineNuxtConfig({
 
  devtools: { enabled: true },
  compatibilityDate: '2024-07-24'
-})
\ No newline at end of file
+})
diff --git a/dbrepo-ui/pages/database/[database_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/settings.vue
index b7cca1b7aa..61e9dc6244 100644
--- a/dbrepo-ui/pages/database/[database_id]/settings.vue
+++ b/dbrepo-ui/pages/database/[database_id]/settings.vue
@@ -366,10 +366,13 @@ export default {
       return this.roles.includes('modify-database-image')
     },
     databaseImage () {
-      if (!this.file) {
+      if (this.file) {
+        return URL.createObjectURL(this.file)
+      }
+      if (!this.database || !this.database.image) {
         return null
       }
-      return URL.createObjectURL(this.file[0])
+      return `data:image/webp;base64,${this.database.image}`
     },
     maxWidth () {
       return this.$config.public.database.image.width
diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue
index 38c9be5567..9da3e1c9fd 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/import.vue
@@ -19,6 +19,7 @@
           vertical
           variant="flat">
           <TableImport
+            :create="false"
             :table-id="$route.params.table_id" />
         </v-stepper>
       </v-card-text>
diff --git a/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue
index 9a68b5786f..045c1932c2 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue
@@ -356,11 +356,17 @@ export default {
           this.loading = false
         })
     },
-    onAnalyse({columns, filename, line_termination}) {
+    onAnalyse({columns, filename, line_termination, separator, skip_lines, quote, null_element, true_element, false_element}) {
       console.debug('analysed', columns)
       this.tableCreate.columns = columns
       this.tableImport.location = filename
       this.tableImport.line_termination = line_termination
+      this.tableImport.separator = separator
+      this.tableImport.skip_lines = skip_lines
+      this.tableImport.quote = quote
+      this.tableImport.null_element = null_element
+      this.tableImport.true_element = true_element
+      this.tableImport.false_element = false_element
       if (filename) {
         this.step = 4
       }
-- 
GitLab