|
15 | 15 |
|
16 | 16 | from sqlalchemy.sql.expression import and_
|
17 | 17 |
|
| 18 | +from sqlalchemy import create_engine, exc |
| 19 | +from sqlalchemy.engine.url import URL |
| 20 | + |
18 | 21 | import codechecker_api_shared
|
19 | 22 | from codechecker_api.ProductManagement_v6 import ttypes
|
20 | 23 |
|
@@ -318,6 +321,57 @@ def getProductConfiguration(self, product_id):
|
318 | 321 |
|
319 | 322 | return prod
|
320 | 323 |
|
| 324 | + @timeit |
| 325 | + def __add_product_support(self, product): |
| 326 | + """ |
| 327 | + Creates a database for the given product, |
| 328 | + to assist addProduct() function that connects to |
| 329 | + an already existing database. |
| 330 | + """ |
| 331 | + |
| 332 | + product_info = product.connection |
| 333 | + if product_info.engine == 'sqlite': |
| 334 | + LOG.info("Using SQLite engine, skipping database creation") |
| 335 | + return True |
| 336 | + |
| 337 | + db_host = product_info.host |
| 338 | + db_engine = product_info.engine |
| 339 | + db_port = int(product_info.port) |
| 340 | + db_user = convert.from_b64(product_info.username_b64) |
| 341 | + db_pass = convert.from_b64(product_info.password_b64) |
| 342 | + db_name = product_info.database |
| 343 | + |
| 344 | + engine_url = URL( |
| 345 | + drivername=db_engine, |
| 346 | + username=db_user, |
| 347 | + password=db_pass, |
| 348 | + host=db_host, |
| 349 | + port=db_port, |
| 350 | + database='postgres' |
| 351 | + ) |
| 352 | + engine = create_engine(engine_url) |
| 353 | + try: |
| 354 | + with engine.connect() as conn: |
| 355 | + conn.execute("commit") |
| 356 | + LOG.info("Creating database '%s'", db_name) |
| 357 | + conn.execute(f"CREATE DATABASE {db_name}") |
| 358 | + conn.close() |
| 359 | + except exc.ProgrammingError as e: |
| 360 | + LOG.error("ProgrammingError occurred: %s", str(e)) |
| 361 | + if "already exists" in str(e): |
| 362 | + LOG.error("Database '%s' already exists", db_name) |
| 363 | + return False |
| 364 | + else: |
| 365 | + LOG.error("Error occurred while creating database: %s", str(e)) |
| 366 | + return False |
| 367 | + except exc.SQLAlchemyError as e: |
| 368 | + LOG.error("SQLAlchemyError occurred: %s", str(e)) |
| 369 | + return False |
| 370 | + finally: |
| 371 | + engine.dispose() |
| 372 | + |
| 373 | + return True |
| 374 | + |
321 | 375 | @timeit
|
322 | 376 | def addProduct(self, product):
|
323 | 377 | """
|
@@ -352,6 +406,19 @@ def addProduct(self, product):
|
352 | 406 | codechecker_api_shared.ttypes.ErrorCode.GENERAL,
|
353 | 407 | msg)
|
354 | 408 |
|
| 409 | + # Check if the database is already in use by another product. |
| 410 | + db_in_use = self.__server.is_database_used(product) |
| 411 | + if db_in_use: |
| 412 | + LOG.error("Database '%s' is already in use by another product!", |
| 413 | + product.connection.database) |
| 414 | + raise codechecker_api_shared.ttypes.RequestFailed( |
| 415 | + codechecker_api_shared.ttypes.ErrorCode.DATABASE, |
| 416 | + "Database is already in use by another product!") |
| 417 | + |
| 418 | + # Add database before letting product connect to it |
| 419 | + if self.__add_product_support(product): |
| 420 | + LOG.info("Database support added successfully.") |
| 421 | + |
355 | 422 | # Some values come encoded as Base64, decode these.
|
356 | 423 | displayed_name = convert.from_b64(product.displayedName_b64) \
|
357 | 424 | if product.displayedName_b64 else product.endpoint
|
|
0 commit comments