diff --git a/hana/lib/HANAService.js b/hana/lib/HANAService.js index 7fc956d9c..81282be39 100644 --- a/hana/lib/HANAService.js +++ b/hana/lib/HANAService.js @@ -1426,9 +1426,21 @@ SELECT ${mixing} FROM JSON_TABLE(SRC.JSON, '$' COLUMNS(${extraction}) ERROR ON E const con = await this.factory.create(this.options.credentials) this.dbc = con - const stmt = await this.dbc.prepare(createContainerDatabase) - const res = this.ensureDBC() && await stmt.run([creds.user, creds.password, creds.containerGroup, !clean]) - res && DEBUG?.(res.changes.map(r => r.MESSAGE).join('\n')) + let i = 0 + let err + for (; i < 100; i++) { + try { + const stmt = await this.dbc.prepare(createContainerDatabase) + const res = this.ensureDBC() && await stmt.run([creds.user, creds.password, creds.containerGroup, !clean]) + Array.isArray(res?.changes) && DEBUG?.(res.changes.map(r => r.MESSAGE).join('\n')) + break + } catch (e) { + err = e + } + } + if (i === 100) { + throw new Error(`Failed to create tenant: ${err.message || err.stack || err}`) + } } finally { if (this.dbc) { // Release table lock @@ -1474,7 +1486,7 @@ SELECT ${mixing} FROM JSON_TABLE(SRC.JSON, '$' COLUMNS(${extraction}) ERROR ON E try { const stmt = await this.dbc.prepare(createContainerTenant.replaceAll('{{{GROUP}}}', creds.containerGroup)) const res = this.ensureDBC() && await stmt.run([creds.user, creds.password, creds.schema, !clean]) - res && DEBUG?.(res.changes.map?.(r => r.MESSAGE).join('\n')) + Array.isArray(res?.changes) && DEBUG?.(res.changes.map(r => r.MESSAGE).join('\n')) break } catch (e) { err = e diff --git a/hana/lib/scripts/container-database.sql b/hana/lib/scripts/container-database.sql index c2b557ffd..cb2287c8a 100644 --- a/hana/lib/scripts/container-database.sql +++ b/hana/lib/scripts/container-database.sql @@ -6,8 +6,7 @@ DO ( IN CREATEGROUP BOOLEAN => ? ) BEGIN SEQUENTIAL EXECUTION - DECLARE USER_EXISTS INT; - DECLARE USER_GROUP_EXISTS INT; + DECLARE CONTAINER_GROUP_EXISTS INT; DECLARE OPERATOR_ROLE NVARCHAR(256); DECLARE RETURN_CODE INT; DECLARE REQUEST_ID BIGINT; @@ -20,40 +19,41 @@ BEGIN SEQUENTIAL EXECUTION NO_PARAMS = SELECT * FROM _SYS_DI.T_NO_PARAMETERS; - SELECT 1 FROM _SYS_DI.T_DEFAULT_CONTAINER_USER_PRIVILEGES FOR UPDATE; -- lock to prevent race conditions - SELECT COUNT(*) INTO USER_EXISTS FROM SYS.USERS WHERE USER_NAME = :USERNAME; - SELECT COUNT(*) INTO USER_GROUP_EXISTS FROM SYS.USERGROUPS WHERE USERGROUP_NAME = :SCHEMANAME || '_USERS'; SELECT NAME INTO OPERATOR_ROLE FROM SYS.PRIVILEGES WHERE NAME = 'OPERATOR' OR NAME = 'USERGROUP OPERATOR'; IF :CREATEGROUP = FALSE THEN + DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; CALL _SYS_DI.DROP_CONTAINER_GROUP(:SCHEMANAME, :NO_PARAMS, :RETURN_CODE, :REQUEST_ID, :MESSAGES); + COMMIT; ALL_MESSAGES = SELECT * FROM :MESSAGES; - IF :USER_EXISTS > 0 THEN - EXEC 'DROP USER ' || :USERNAME || ' CASCADE'; - END IF; + EXEC 'DROP USER ' || :USERNAME || ' CASCADE'; END IF; IF :CREATEGROUP = TRUE THEN - IF :USER_GROUP_EXISTS = 0 THEN + BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; -- try catch EXEC 'CREATE USERGROUP "' || :SCHEMANAME || '_USERS"'; - END IF; - IF :USER_EXISTS = 0 THEN + END; + BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; -- try catch EXEC 'CREATE USER ' || :USERNAME || ' PASSWORD ' || :USERPASS || ' NO FORCE_FIRST_PASSWORD_CHANGE SET USERGROUP "' || :SCHEMANAME || '_USERS"'; - ELSE EXEC 'ALTER USER ' || :USERNAME || ' DISABLE PASSWORD LIFETIME'; + EXEC 'GRANT ' || :OPERATOR_ROLE || ' ON USERGROUP "' || :SCHEMANAME || '_USERS" TO ' || :USERNAME; + EXEC 'GRANT EXECUTE ON SYS.GET_INSUFFICIENT_PRIVILEGE_ERROR_DETAILS TO ' || :USERNAME || ' WITH GRANT OPTION'; + EXEC 'GRANT OPTIMIZER ADMIN TO ' || :USERNAME || ' WITH ADMIN OPTION'; + END; + + SELECT COUNT(*) INTO CONTAINER_GROUP_EXISTS FROM _SYS_DI.M_ALL_CONTAINER_GROUPS WHERE CONTAINER_GROUP_NAME = :SCHEMANAME; + IF :CONTAINER_GROUP_EXISTS = 0 THEN + CALL _SYS_DI.CREATE_CONTAINER_GROUP(:SCHEMANAME, :NO_PARAMS, :RETURN_CODE, :REQUEST_ID, :MESSAGES); + COMMIT; + ALL_MESSAGES = SELECT * FROM :MESSAGES; + + PRIVILEGES = SELECT PRIVILEGE_NAME, OBJECT_NAME, PRINCIPAL_SCHEMA_NAME, :USERNAME AS PRINCIPAL_NAME FROM _SYS_DI.T_DEFAULT_CONTAINER_GROUP_ADMIN_PRIVILEGES; + CALL _SYS_DI.GRANT_CONTAINER_GROUP_API_PRIVILEGES(:SCHEMANAME, :PRIVILEGES, :NO_PARAMS, :RETURN_CODE, :REQUEST_ID, :MESSAGES); + COMMIT; + ALL_MESSAGES = SELECT * FROM :ALL_MESSAGES UNION ALL SELECT * FROM :MESSAGES; END IF; - EXEC 'GRANT ' || :OPERATOR_ROLE || ' ON USERGROUP "' || :SCHEMANAME || '_USERS" TO ' || :USERNAME; - EXEC 'GRANT EXECUTE ON SYS.GET_INSUFFICIENT_PRIVILEGE_ERROR_DETAILS TO ' || :USERNAME || ' WITH GRANT OPTION'; - EXEC 'GRANT OPTIMIZER ADMIN TO ' || :USERNAME || ' WITH ADMIN OPTION'; - CALL _SYS_DI.CREATE_CONTAINER_GROUP(:SCHEMANAME, :NO_PARAMS, :RETURN_CODE, :REQUEST_ID, :MESSAGES); - ALL_MESSAGES = SELECT * FROM :MESSAGES; - COMMIT; - - PRIVILEGES = SELECT PRIVILEGE_NAME, OBJECT_NAME, PRINCIPAL_SCHEMA_NAME, :USERNAME AS PRINCIPAL_NAME FROM _SYS_DI.T_DEFAULT_CONTAINER_GROUP_ADMIN_PRIVILEGES; - - CALL _SYS_DI.GRANT_CONTAINER_GROUP_API_PRIVILEGES(:SCHEMANAME, :PRIVILEGES, :NO_PARAMS, :RETURN_CODE, :REQUEST_ID, :MESSAGES); - ALL_MESSAGES = SELECT * FROM :ALL_MESSAGES UNION ALL SELECT * FROM :MESSAGES; - COMMIT; SELECT * FROM :ALL_MESSAGES; END IF; COMMIT;