V(mutex);
return mdb;
}
-
+
+bool BDB_MYSQL::is_pkey_required()
+{
+ int ret = mysql_query(m_db_handle, "show variables like 'sql_require_primary_key'");
+ bool req_pkey = false;
+ if (!ret) {
+ if ((m_result = mysql_use_result(m_db_handle)) != NULL) {
+ SQL_ROW row;
+ while ((row = mysql_fetch_row(m_result))) {
+ req_pkey = !strncmp(row[1], "ON", 2);
+ }
+ sql_free_result();
+ }
+ }
+ return req_pkey;
+}
/*
* Now actually open the database. This can generate errors,
if (!bdb_check_version(jcr)) {
goto get_out;
}
+
+ /* Check if all tables created must have a Primary Key */
+ if (is_pkey_required()) {
+ m_pkey_query_buffer = get_pool_memory(PM_FNAME);
+ } else {
+ m_pkey_query_buffer = NULL;
+ }
Dmsg3(100, "opendb ref=%d connected=%d db=%p\n", mdb->m_ref_count, mdb->m_connected, mdb->m_db_handle);
free_pool_memory(mdb->esc_name);
free_pool_memory(mdb->esc_path);
free_pool_memory(mdb->esc_obj);
+ if (mdb->m_pkey_query_buffer) {
+ free_pool_memory(mdb->m_pkey_query_buffer);
+ }
if (mdb->m_db_driver) {
free(mdb->m_db_driver);
}
void BDB_MYSQL::bdb_end_transaction(JCR *jcr)
{
}
-
+
+/*
+ * Helper function to dynamically add Primary Keys to the tables being created.
+ */
+const char* BDB_MYSQL::enable_pkey(const char *query)
+{
+ if (!m_pkey_query_buffer) return query;
+ const char *tag = strstr(query, "/*PKEY");
+ if (!tag) return query;
+
+ pm_strcpy(m_pkey_query_buffer, query);
+ char *pos = strstr(m_pkey_query_buffer, "/*PKEY");
+ for (int i=0; i<6; i++) *pos++=' ';
+ pos = strstr(pos, "*/");
+ for (int i=0; i<2; i++) *pos++=' ';
+ return m_pkey_query_buffer;
+}
+
/*
* Submit a general SQL command (cmd), and for each row returned,
* the result_handler is called with the ctx.
bdb_lock();
errmsg[0] = 0;
+
+ query = enable_pkey(query);
ret = mysql_query(m_db_handle, query);
if (ret != 0) {
Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror());
- Dmsg0(500, "db_sql_query failed\n");
+ Dmsg0(500, "db_sql_query failed\n");
goto get_out;
}
bdb_unlock();
return retval;
}
-
+
bool BDB_MYSQL::sql_query(const char *query, int flags)
{
int ret;
mdb->m_result = NULL;
}
+ query = enable_pkey(query);
ret = mysql_query(mdb->m_db_handle, query);
if (ret == 0) {
Dmsg0(500, "we have a result\n");
bdb_lock();
retval = sql_query("CREATE TEMPORARY TABLE batch ("
- "FileIndex integer,"
- "JobId integer,"
+ "FileIndex integer not null,"
+ "JobId integer not null,"
"Path blob,"
"Name blob,"
"LStat tinyblob,"
"MD5 tinyblob,"
- "DeltaSeq integer)");
+ "DeltaSeq integer"
+ "/*PKEY, DummyPkey INTEGER AUTO_INCREMENT PRIMARY KEY*/)");
bdb_unlock();
/*
* Try to batch up multiple inserts using multi-row inserts.
*/
if (mdb->changes == 0) {
- Mmsg(cmd, "INSERT INTO batch VALUES "
+ Mmsg(cmd, "INSERT INTO batch(FileIndex, JobId, Path, Name, LStat, MD5, DeltaSeq) VALUES "
"(%d,%s,'%s','%s','%s','%s',%u)",
ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path,
mdb->esc_name, ar->attr, digest, ar->DeltaSeq);
const char *uar_del_temp1 = "DROP TABLE IF EXISTS temp1";
const char *uar_last_full =
- "INSERT INTO temp1 SELECT Job.JobId,JobTdate "
+ "INSERT INTO temp1(JobId,JobTDate) SELECT Job.JobId,JobTdate "
"FROM Client,Job,JobMedia,Media,FileSet WHERE Client.ClientId=%s "
"AND Job.ClientId=%s "
"AND Job.StartTime < '%s' "
"ORDER BY Job.JobTDate DESC LIMIT 1";
const char *uar_full =
- "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,"
+ "INSERT INTO temp (JobId,JobTDate,ClientId,Level,JobFiles,JobBytes,StartTime,VolumeName,StartFile,VolSessionId,VolSessionTime) "
+
+ "SELECT Job.JobId,Job.JobTDate,"
"Job.ClientId,Job.Level,Job.JobFiles,Job.JobBytes,"
"StartTime,VolumeName,JobMedia.StartFile,VolSessionId,VolSessionTime "
"FROM temp1,Job,JobMedia,Media WHERE temp1.JobId=Job.JobId "
"AND JobMedia.MediaId=Media.MediaId";
const char *uar_dif =
- "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
+ "INSERT INTO temp (JobId,JobTDate,ClientId,Level,JobFiles,JobBytes,StartTime,VolumeName,StartFile,VolSessionId,VolSessionTime) "
+ "SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
"Job.Level,Job.JobFiles,Job.JobBytes,"
"Job.StartTime,Media.VolumeName,JobMedia.StartFile,"
"Job.VolSessionId,Job.VolSessionTime "
"ORDER BY Job.JobTDate DESC LIMIT 1";
const char *uar_inc =
- "INSERT INTO temp SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
+ "INSERT INTO temp (JobId,JobTDate,ClientId,Level,JobFiles,JobBytes,StartTime,VolumeName,StartFile,VolSessionId,VolSessionTime) "
+ "SELECT Job.JobId,Job.JobTDate,Job.ClientId,"
"Job.Level,Job.JobFiles,Job.JobBytes,"
"Job.StartTime,Media.VolumeName,JobMedia.StartFile,"
"Job.VolSessionId,Job.VolSessionTime "
/* We don't create this table as TEMPORARY because MySQL
MyISAM 5.0 and 5.1 are unable to run further queries in this mode
*/
-static const char *create_temp_accurate_jobids_default =
- "CREATE TABLE btemp3%s AS "
- "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles "
+static const char *create_temp_accurate_jobids_default =
+ "CREATE TABLE btemp3%s AS "
+ "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles, FileSetId "
"FROM Job JOIN FileSet USING (FileSetId) "
"WHERE ClientId = %s "
"AND Level='F' AND JobStatus IN ('T','W') AND Type='B' "
"AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
" %s " /* Any filter */
"ORDER BY Job.JobTDate DESC LIMIT 1";
-
+
+static const char *create_temp_accurate_jobids_mysql =
+ "CREATE TABLE btemp3%s /*PKEY (DummyPkey INTEGER AUTO_INCREMENT PRIMARY KEY)*/ AS ("
+ "SELECT JobId, StartTime, EndTime, JobTDate, PurgedFiles, FileSetId "
+ "FROM Job JOIN FileSet USING (FileSetId) "
+ "WHERE ClientId = %s "
+ "AND Level='F' AND JobStatus IN ('T','W') AND Type='B' "
+ "AND StartTime<'%s' "
+ "AND FileSet.FileSet=(SELECT FileSet FROM FileSet WHERE FileSetId = %s) "
+ " %s " /* Any filter */
+ "ORDER BY Job.JobTDate DESC LIMIT 1)";
+
const char *create_temp_accurate_jobids[] =
{
/* MySQL */
- create_temp_accurate_jobids_default,
+ create_temp_accurate_jobids_mysql,
/* PostgreSQL */
- create_temp_accurate_jobids_default,
+ create_temp_accurate_jobids_default,
/* SQLite */
create_temp_accurate_jobids_default
-};
+};
const char *create_temp_basefile[] =
{
/* MySQL */
"CREATE TEMPORARY TABLE basefile%lld"
- "(Path BLOB NOT NULL, Name BLOB NOT NULL,"
- " INDEX (Path(255), Name(255)))",
+ "(Path BLOB NOT NULL, Name BLOB NOT NULL, "
+ "INDEX (Path(255), Name(255))"
+ "/*PKEY, DummyPkey INTEGER AUTO_INCREMENT PRIMARY KEY*/)",
/* PostgreSQL */
"CREATE TEMPORARY TABLE basefile%lld"
"(Path TEXT, Name TEXT)",
const char *create_temp_new_basefile[] =
{
/* MySQL */
- "CREATE TEMPORARY TABLE new_basefile%lld AS "
+ "CREATE TEMPORARY TABLE new_basefile%lld /*PKEY(DummyPkey INTEGER AUTO_INCREMENT PRIMARY KEY)*/ AS "
"SELECT Path.Path AS Path, Temp.Filename AS Name, Temp.FileIndex AS FileIndex,"
" Temp.JobId AS JobId, Temp.LStat AS LStat, Temp.FileId AS FileId,"
" Temp.MD5 AS MD5"
"PurgedFiles TINYINT, "
"FileSetId INTEGER UNSIGNED, "
"JobFiles INTEGER UNSIGNED, "
- "JobStatus BINARY(1))",
+ "JobStatus BINARY(1)"
+ "/*PKEY, DummyPkey INTEGER AUTO_INCREMENT PRIMARY KEY*/)",
/* PostgreSQL */
"CREATE TEMPORARY TABLE DelCandidates ( "
* to a Backup job when the original backup job is expired.
*/
static const char *uap_upgrade_copies_oldest_job_default =
-"CREATE TEMPORARY TABLE cpy_tmp AS "
+ "CREATE TEMPORARY TABLE cpy_tmp /*PKEY (DummyPkey INTEGER PRIMARY KEY DEFAULT 1)*/ AS "
"SELECT MIN(JobId) AS JobId FROM Job " /* Choose the oldest job */
"WHERE Type='%c' " /* JT_JOB_COPY */
"AND ( PriorJobId IN (%s) " /* JobId selection */
"VolumeName TEXT,"
"StartFile INTEGER UNSIGNED,"
"VolSessionId INTEGER UNSIGNED,"
- "VolSessionTime INTEGER UNSIGNED)",
+ "VolSessionTime INTEGER UNSIGNED"
+ "/*PKEY, DummyPKey INTEGER AUTO_INCREMENT PRIMARY KEY*/)",
/* PostgreSQL */
"CREATE TEMPORARY TABLE temp ("
/* MySQL */
"CREATE TEMPORARY TABLE temp1 ("
"JobId INTEGER UNSIGNED NOT NULL,"
- "JobTDate BIGINT UNSIGNED)",
+ "JobTDate BIGINT UNSIGNED"
+ "/*PKEY, DummyPKey INTEGER AUTO_INCREMENT PRIMARY KEY*/)",
/* PostgreSQL */
"CREATE TEMPORARY TABLE temp1 ("
"JobId INTEGER NOT NULL,"
* version of a file in the same dataset.
*/
const char *default_sql_bvfs_select =
-"CREATE TABLE %s AS "
+"CREATE TABLE %s AS"
+"SELECT File.JobId, File.FileIndex, File.FileId "
+"FROM Job, File, ( "
+ "SELECT MAX(JobTDate) AS JobTDate, PathId, Filename "
+ "FROM btemp%s GROUP BY PathId, Filename "
+ ") AS T1 "
+"WHERE T1.JobTDate = Job.JobTDate "
+ "AND Job.JobId = File.JobId "
+ "AND T1.PathId = File.PathId "
+ "AND T1.Filename = File.Filename "
+ "AND File.FileIndex > 0 "
+ "AND Job.JobId IN (SELECT DISTINCT JobId FROM btemp%s) ";
+
+const char *default_sql_bvfs_select_mysql =
+"CREATE TABLE %s /*PKEY (DummyPkey INTEGER AUTO_INCREMENT PRIMARY KEY)*/ AS "
"SELECT File.JobId, File.FileIndex, File.FileId "
"FROM Job, File, ( "
"SELECT MAX(JobTDate) AS JobTDate, PathId, Filename "
const char *sql_bvfs_select[] =
{
/* MySQL */
- default_sql_bvfs_select,
+ default_sql_bvfs_select_mysql,
/* PostgreSQL */
"CREATE TABLE %s AS ( "
"SELECT JobId, FileIndex, FileId "