PageRenderTime 53ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/src/database/pgsql_common.c

https://github.com/cfenoy/slurm
C | 437 lines | 339 code | 51 blank | 47 comment | 56 complexity | 2ddde530a4c4b645ecc4c789d74aaeb1 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0
  1. /*****************************************************************************\
  2. * pgsql_common.c - common functions for the pgsql storage plugin.
  3. *****************************************************************************
  4. *
  5. * Copyright (C) 2004-2007 The Regents of the University of California.
  6. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  7. * Written by Danny Auble <da@llnl.gov>
  8. *
  9. * This file is part of SLURM, a resource management program.
  10. * For details, see <http://www.schedmd.com/slurmdocs/>.
  11. * Please also read the included file: DISCLAIMER.
  12. *
  13. * SLURM is free software; you can redistribute it and/or modify it under
  14. * the terms of the GNU General Public License as published by the Free
  15. * Software Foundation; either version 2 of the License, or (at your option)
  16. * any later version.
  17. *
  18. * In addition, as a special exception, the copyright holders give permission
  19. * to link the code of portions of this program with the OpenSSL library under
  20. * certain conditions as described in each individual source file, and
  21. * distribute linked combinations including the two. You must obey the GNU
  22. * General Public License in all respects for all of the code used other than
  23. * OpenSSL. If you modify file(s) with this exception, you may extend this
  24. * exception to your version of the file(s), but you are not obligated to do
  25. * so. If you do not wish to do so, delete this exception statement from your
  26. * version. If you delete this exception statement from all source files in
  27. * the program, then also delete it here.
  28. *
  29. * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
  30. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  31. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  32. * details.
  33. *
  34. * You should have received a copy of the GNU General Public License along
  35. * with SLURM; if not, write to the Free Software Foundation, Inc.,
  36. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  37. *
  38. * This file is patterned after jobcomp_linux.c, written by Morris Jette and
  39. * Copyright (C) 2002 The Regents of the University of California.
  40. \*****************************************************************************/
  41. #include "pgsql_common.h"
  42. #include <stdlib.h>
  43. pthread_mutex_t pgsql_lock = PTHREAD_MUTEX_INITIALIZER;
  44. extern int *destroy_pgsql_db_info(pgsql_db_info_t *db_info)
  45. {
  46. if (db_info) {
  47. xfree(db_info->host);
  48. xfree(db_info->backup);
  49. xfree(db_info->user);
  50. xfree(db_info->pass);
  51. xfree(db_info);
  52. }
  53. return SLURM_SUCCESS;
  54. }
  55. extern int _create_db(char *db_name, pgsql_db_info_t *db_info)
  56. {
  57. char create_line[50];
  58. PGconn *pgsql_db = NULL;
  59. char *connect_line = xstrdup_printf("dbname = 'postgres'"
  60. " host = '%s'"
  61. " port = '%u'"
  62. " user = '%s'"
  63. " password = '%s'",
  64. db_info->host,
  65. db_info->port,
  66. db_info->user,
  67. db_info->pass);
  68. pgsql_db = PQconnectdb(connect_line);
  69. if (PQstatus(pgsql_db) == CONNECTION_OK) {
  70. PGresult *result = NULL;
  71. snprintf(create_line, sizeof(create_line),
  72. "create database %s", db_name);
  73. result = PQexec(pgsql_db, create_line);
  74. if (PQresultStatus(result) != PGRES_COMMAND_OK) {
  75. fatal("PQexec failed: %d %s\n%s",
  76. PQresultStatus(result), PQerrorMessage(pgsql_db),
  77. create_line);
  78. }
  79. PQclear(result);
  80. pgsql_close_db_connection(&pgsql_db);
  81. } else {
  82. info("Connection failed to %s", connect_line);
  83. fatal("Status was: %d %s",
  84. PQstatus(pgsql_db), PQerrorMessage(pgsql_db));
  85. }
  86. xfree(connect_line);
  87. return SLURM_SUCCESS;
  88. }
  89. extern int pgsql_get_db_connection(PGconn **pgsql_db, char *db_name,
  90. pgsql_db_info_t *db_info)
  91. {
  92. int rc = SLURM_SUCCESS;
  93. bool storage_init = false;
  94. char *connect_line = xstrdup_printf("dbname = '%s'"
  95. " host = '%s'"
  96. " port = '%u'"
  97. " user = '%s'"
  98. " password = '%s'",
  99. db_name,
  100. db_info->host,
  101. db_info->port,
  102. db_info->user,
  103. db_info->pass);
  104. while (!storage_init) {
  105. //debug2("pgsql connect: %s", connect_line);
  106. *pgsql_db = PQconnectdb(connect_line);
  107. if (PQstatus(*pgsql_db) != CONNECTION_OK) {
  108. if (!strcmp(PQerrorMessage(*pgsql_db),
  109. "no password supplied")) {
  110. PQfinish(*pgsql_db);
  111. fatal("This Postgres connection needs "
  112. "a password. It doesn't appear to "
  113. "like blank ones");
  114. }
  115. info("Database %s not created. Creating", db_name);
  116. pgsql_close_db_connection(pgsql_db);
  117. _create_db(db_name, db_info);
  118. } else {
  119. char *language_line = "CREATE LANGUAGE plpgsql;";
  120. PGresult *result = NULL;
  121. const char *ver;
  122. ver = PQparameterStatus(*pgsql_db, "server_version");
  123. if (atof(ver) < 8.3)
  124. fatal("server version 8.3 or above required");
  125. /* This needs to be done for the accounting_storage
  126. plugin, and most likely any pgsql plugin. */
  127. result = PQexec(*pgsql_db, language_line);
  128. if ((PQresultStatus(result) != PGRES_COMMAND_OK)
  129. && strcmp("ERROR: language \"plpgsql\" "
  130. "already exists\n",
  131. PQerrorMessage(*pgsql_db))) {
  132. fatal("PQexec failed: %d %s\n%s",
  133. PQresultStatus(result),
  134. PQerrorMessage(*pgsql_db),
  135. language_line);
  136. }
  137. PQclear(result);
  138. storage_init = true;
  139. }
  140. }
  141. xfree(connect_line);
  142. return rc;
  143. }
  144. extern int pgsql_close_db_connection(PGconn **pgsql_db)
  145. {
  146. if (pgsql_db && *pgsql_db) {
  147. PQfinish(*pgsql_db);
  148. *pgsql_db = NULL;
  149. }
  150. return SLURM_SUCCESS;
  151. }
  152. extern int pgsql_db_query(PGconn *pgsql_db, char *query)
  153. {
  154. PGresult *result = NULL;
  155. if (!pgsql_db)
  156. fatal("You haven't inited this storage yet.");
  157. if (!(result = pgsql_db_query_ret(pgsql_db, query)))
  158. return SLURM_ERROR;
  159. PQclear(result);
  160. return SLURM_SUCCESS;
  161. }
  162. extern int pgsql_db_start_transaction(PGconn *pgsql_db)
  163. {
  164. return pgsql_db_query(pgsql_db, "BEGIN WORK");
  165. }
  166. extern int pgsql_db_commit(PGconn *pgsql_db)
  167. {
  168. return pgsql_db_query(pgsql_db, "COMMIT WORK");
  169. }
  170. extern int pgsql_db_rollback(PGconn *pgsql_db)
  171. {
  172. return pgsql_db_query(pgsql_db, "ROLLBACK WORK");
  173. }
  174. extern PGresult *pgsql_db_query_ret(PGconn *pgsql_db, char *query)
  175. {
  176. PGresult *result = NULL;
  177. if (!pgsql_db)
  178. fatal("You haven't inited this storage yet.");
  179. result = PQexec(pgsql_db, query);
  180. if (PQresultStatus(result) != PGRES_COMMAND_OK
  181. && PQresultStatus(result) != PGRES_TUPLES_OK) {
  182. error("PQexec failed: %d %s", PQresultStatus(result),
  183. PQerrorMessage(pgsql_db));
  184. info("query was %s", query);
  185. PQclear(result);
  186. return NULL;
  187. }
  188. return result;
  189. }
  190. extern int pgsql_insert_ret_id(PGconn *pgsql_db, char *sequence_name,
  191. char *query)
  192. {
  193. int new_id = 0;
  194. PGresult *result = NULL;
  195. slurm_mutex_lock(&pgsql_lock);
  196. if (pgsql_db_query(pgsql_db, query) != SLURM_ERROR) {
  197. char *new_query = xstrdup_printf(
  198. "select last_value from %s", sequence_name);
  199. if ((result = pgsql_db_query_ret(pgsql_db, new_query))) {
  200. new_id = atoi(PQgetvalue(result, 0, 0));
  201. PQclear(result);
  202. }
  203. xfree(new_query);
  204. if (!new_id) {
  205. /* should have new id */
  206. error("We should have gotten a new id: %s",
  207. PQerrorMessage(pgsql_db));
  208. }
  209. }
  210. slurm_mutex_unlock(&pgsql_lock);
  211. return new_id;
  212. }
  213. extern int pgsql_query_ret_id(PGconn *pgsql_db, char *query)
  214. {
  215. int new_id = 0;
  216. PGresult *result = NULL;
  217. slurm_mutex_lock(&pgsql_lock);
  218. result = pgsql_db_query_ret(pgsql_db, query);
  219. if (result) {
  220. new_id = atoi(PQgetvalue(result, 0, 0));
  221. PQclear(result);
  222. } else {
  223. /* should have new id */
  224. error("pgsql_query_ret_id() query failed: %s",
  225. PQerrorMessage(pgsql_db));
  226. }
  227. slurm_mutex_unlock(&pgsql_lock);
  228. return new_id;
  229. }
  230. extern int pgsql_db_create_table(PGconn *pgsql_db, char *schema,
  231. char *table_name, storage_field_t *fields,
  232. char *ending)
  233. {
  234. char *query = NULL;
  235. char *tmp = NULL;
  236. char *next = NULL;
  237. int i = 0;
  238. query = xstrdup_printf("create table %s.%s (", schema, table_name);
  239. i=0;
  240. while (fields && fields->name) {
  241. next = xstrdup_printf(" %s %s",
  242. fields->name,
  243. fields->options);
  244. if (i)
  245. xstrcat(tmp, ",");
  246. xstrcat(tmp, next);
  247. xfree(next);
  248. fields++;
  249. i++;
  250. }
  251. xstrcat(query, tmp);
  252. xfree(tmp);
  253. xstrcat(query, ending);
  254. if (pgsql_db_query(pgsql_db, query) == SLURM_ERROR) {
  255. xfree(query);
  256. return SLURM_ERROR;
  257. }
  258. xfree(query);
  259. return SLURM_SUCCESS;
  260. }
  261. extern int
  262. pgsql_db_make_table_current(PGconn *pgsql_db, char *schema, char *table_name,
  263. storage_field_t *fields)
  264. {
  265. char *query = NULL, *opt_part = NULL, *temp_char = NULL;
  266. char *type = NULL;
  267. int not_null = 0;
  268. char *default_str = NULL;
  269. char* original_ptr = NULL;
  270. int i = 0;
  271. PGresult *result = NULL;
  272. List columns = NULL;
  273. ListIterator itr = NULL;
  274. char *col = NULL;
  275. DEF_TIMERS;
  276. query = xstrdup_printf("select column_name from "
  277. "information_schema.columns where "
  278. "table_name='%s' and table_schema='%s' ",
  279. table_name, schema);
  280. if (!(result = pgsql_db_query_ret(pgsql_db, query))) {
  281. xfree(query);
  282. return SLURM_ERROR;
  283. }
  284. xfree(query);
  285. columns = list_create(slurm_destroy_char);
  286. for (i = 0; i < PQntuples(result); i++) {
  287. col = xstrdup(PQgetvalue(result, i, 0)); //column_name
  288. list_append(columns, col);
  289. }
  290. PQclear(result);
  291. itr = list_iterator_create(columns);
  292. query = xstrdup_printf("alter table %s.%s", schema, table_name);
  293. START_TIMER;
  294. i=0;
  295. while (fields[i].name) {
  296. int found = 0;
  297. not_null = 0;
  298. if (!strcasecmp("serial", fields[i].options)) {
  299. i++;
  300. continue;
  301. }
  302. opt_part = xstrdup(fields[i].options);
  303. original_ptr = opt_part;
  304. opt_part = strtok_r(opt_part, " ", &temp_char);
  305. if (opt_part) {
  306. /* XXX: only one identifier supported */
  307. type = xstrdup(opt_part);
  308. opt_part = strtok_r(NULL, " ", &temp_char);
  309. while (opt_part) {
  310. if (!strcasecmp("not", opt_part)) {
  311. opt_part = strtok_r(NULL, " ",
  312. &temp_char);
  313. if (!strcasecmp("null", opt_part)) {
  314. not_null = 1;
  315. }
  316. } else if (!strcasecmp("default", opt_part)){
  317. opt_part = strtok_r(NULL,
  318. " ", &temp_char);
  319. default_str = xstrdup(opt_part);
  320. }
  321. opt_part = strtok_r(NULL,
  322. " ", &temp_char);
  323. }
  324. } else {
  325. type = xstrdup(fields[i].options);
  326. }
  327. xfree(original_ptr);
  328. list_iterator_reset(itr);
  329. while ((col = list_next(itr))) {
  330. if (!strcmp(col, fields[i].name)) {
  331. list_delete_item(itr);
  332. found = 1;
  333. break;
  334. }
  335. }
  336. temp_char = NULL;
  337. if (!found) {
  338. info("adding column %s", fields[i].name);
  339. if (default_str)
  340. xstrfmtcat(temp_char,
  341. " default %s", default_str);
  342. if (not_null)
  343. xstrcat(temp_char, " not null");
  344. xstrfmtcat(query,
  345. " add %s %s",
  346. fields[i].name, type);
  347. if (temp_char)
  348. xstrcat(query, temp_char);
  349. xstrcat(query, ",");
  350. } else {
  351. if (default_str)
  352. xstrfmtcat(temp_char,
  353. " alter %s set default %s,",
  354. fields[i].name, default_str);
  355. else
  356. xstrfmtcat(temp_char,
  357. " alter %s drop default,",
  358. fields[i].name);
  359. if (not_null)
  360. xstrfmtcat(temp_char,
  361. " alter %s set not null,",
  362. fields[i].name);
  363. else
  364. xstrfmtcat(temp_char,
  365. " alter %s drop not null,",
  366. fields[i].name);
  367. xstrfmtcat(query, " alter %s type %s,%s",
  368. fields[i].name, type, temp_char);
  369. }
  370. xfree(temp_char);
  371. xfree(default_str);
  372. xfree(type);
  373. i++;
  374. }
  375. list_iterator_destroy(itr);
  376. list_destroy(columns);
  377. query[strlen(query)-1] = ';';
  378. //debug4("pgsql db create/alter table:\n %s", query);
  379. if (pgsql_db_query(pgsql_db, query)) {
  380. xfree(query);
  381. return SLURM_ERROR;
  382. }
  383. xfree(query);
  384. END_TIMER2("make table current");
  385. return SLURM_SUCCESS;
  386. }