Skip to content
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e5dcad6
fix: change to read committed
sattvikc Nov 27, 2025
0125d22
fix: new tables for reservation
sattvikc Dec 8, 2025
3234b87
fix: signup
sattvikc Dec 9, 2025
ad7d6b2
fix: refactor account info queries
sattvikc Dec 10, 2025
0f48674
fix: refactor account info queries
sattvikc Dec 10, 2025
2930e09
fix: can and create primary
sattvikc Dec 11, 2025
98582a7
fix: link accounts
sattvikc Dec 12, 2025
854a1a3
fix: associate tenant
sattvikc Dec 12, 2025
b6b5b2c
fix: tenant disassociation
sattvikc Dec 18, 2025
27f3ded
fix: delete user and bug fixes
sattvikc Dec 18, 2025
4c211e0
fix: tp fix
sattvikc Dec 18, 2025
768ecb1
fix: fkey
sattvikc Dec 19, 2025
c33b8d5
fix: unlink accounts and bug fixes
sattvikc Dec 19, 2025
df5c584
fix: email change
sattvikc Dec 24, 2025
0d4cd8d
fix: bulk import
sattvikc Dec 26, 2025
e2868b1
fix: compile
sattvikc Dec 26, 2025
ae89e02
fix: index
sattvikc Dec 30, 2025
854414b
fix: using results for can make primary and can link
sattvikc Jan 2, 2026
5e69e02
fix: bulk primary and link accounts
sattvikc Jan 5, 2026
a050582
fix: update plugin version
sattvikc Jan 6, 2026
2f3490f
fix: review comments
sattvikc Jan 7, 2026
cc411fb
fix: refactor create primary
sattvikc Jan 7, 2026
a135aac
fix: account info table
sattvikc Jan 12, 2026
bae0bc6
fix: bulk import impl
sattvikc Jan 14, 2026
77ec671
fix: review comments
sattvikc Jan 19, 2026
5edff93
fix: bulk import error handling
sattvikc Jan 19, 2026
4b2a0c6
fix: cleanup
sattvikc Jan 19, 2026
0675cd7
fix: cleanup
sattvikc Jan 20, 2026
e86ed96
fix: deadlock tests
sattvikc Jan 21, 2026
e9019bc
fix: deadlock tests
sattvikc Jan 21, 2026
21b3043
Merge remote-tracking branch 'origin/9.4' into feat/isolation-reserva…
porcellus Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
id 'java-library'
}

version = "9.3.0"
version = "9.4.0"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion pluginInterfaceSupported.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"_comment": "contains a list of plugin interfaces branch names that this core supports",
"versions": [
"8.3"
"8.4"
]
}
27 changes: 27 additions & 0 deletions src/main/java/io/supertokens/storage/postgresql/LockFailure.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
* "License") as published by the Apache Software Foundation.
*
* You may not use this file except in compliance with the License. You may
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package io.supertokens.storage.postgresql;

public class LockFailure extends Exception {
public LockFailure() {
super("Failed to acquire advisory lock");
}

public LockFailure(String message) {
super(message);
}
}
484 changes: 315 additions & 169 deletions src/main/java/io/supertokens/storage/postgresql/Start.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,17 @@ public String getBulkImportUsersTable() {
return addSchemaAndPrefixToTableName("bulk_import_users");
}

public String getRecipeUserAccountInfosTable() {
return addSchemaAndPrefixToTableName("recipe_user_account_infos");
}

public String getRecipeUserTenantsTable() {
return addSchemaAndPrefixToTableName("recipe_user_tenants");
}

public String getPrimaryUserTenantsTable() {
return addSchemaAndPrefixToTableName("primary_user_tenants");
}

private String addSchemaAndPrefixToTableName(String tableName) {
return addSchemaToTableName(postgresql_table_names_prefix + tableName);
Expand Down
1,293 changes: 1,293 additions & 0 deletions src/main/java/io/supertokens/storage/postgresql/queries/AccountInfoQueries.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,39 @@

package io.supertokens.storage.postgresql.queries;

import static java.lang.System.currentTimeMillis;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static io.supertokens.pluginInterface.RECIPE_ID.EMAIL_PASSWORD;
import io.supertokens.pluginInterface.RowMapper;
import io.supertokens.pluginInterface.authRecipe.ACCOUNT_INFO_TYPE;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.exceptions.UnknownUserIdException;
import io.supertokens.pluginInterface.emailpassword.EmailPasswordImportUser;
import io.supertokens.pluginInterface.emailpassword.PasswordResetTokenInfo;
import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.storage.postgresql.PreparedStatementValueSetter;
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute;
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.executeBatch;
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update;
import io.supertokens.storage.postgresql.Start;
import io.supertokens.storage.postgresql.config.Config;
import io.supertokens.storage.postgresql.utils.Utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

import static io.supertokens.pluginInterface.RECIPE_ID.EMAIL_PASSWORD;
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.*;
import static io.supertokens.storage.postgresql.config.Config.getConfig;
import static java.lang.System.currentTimeMillis;
import io.supertokens.storage.postgresql.utils.Utils;

public class EmailPasswordQueries {
static String getQueryToCreateUsersTable(Start start) {
Expand Down Expand Up @@ -309,6 +317,11 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden
});
}

{ // recipe_user_tenants
AccountInfoQueries.addRecipeUserAccountInfo_Transaction(start, sqlCon, tenantIdentifier, userId,
EMAIL_PASSWORD.toString(), ACCOUNT_INFO_TYPE.EMAIL, "", "", email);
}

{ // emailpassword_users
String QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUsersTable()
+ "(app_id, user_id, email, password_hash, time_joined)" + " VALUES(?, ?, ?, ?, ?)";
Expand Down Expand Up @@ -345,16 +358,16 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden
});
}

public static void signUpMultipleForBulkImport_Transaction(Start start, Connection sqlCon, List<EmailPasswordImportUser> usersToSignUp)
public static void importUsers_Transaction(Start start, Connection sqlCon, List<EmailPasswordImportUser> usersToSignUp)
throws StorageQueryException, StorageTransactionLogicException {
try {
String app_id_to_user_id_QUERY = "INSERT INTO " + getConfig(start).getAppIdToUserIdTable()
+ "(app_id, user_id, primary_or_recipe_user_id, recipe_id)" + " VALUES(?, ?, ?, ?)";
+ "(app_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id)" + " VALUES(?, ?, ?, ?, ?)";

String all_auth_recipe_users_QUERY = "INSERT INTO " + getConfig(start).getUsersTable() +
"(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " +
"(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, " +
"primary_or_recipe_user_time_joined)" +
" VALUES(?, ?, ?, ?, ?, ?, ?)";
" VALUES(?, ?, ?, ?, ?, ?, ?, ?)";

String emailpassword_users_QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUsersTable()
+ "(app_id, user_id, email, password_hash, time_joined)" + " VALUES(?, ?, ?, ?, ?)";
Expand All @@ -363,48 +376,68 @@ public static void signUpMultipleForBulkImport_Transaction(Start start, Connecti
"INSERT INTO " + getConfig(start).getEmailPasswordUserToTenantTable()
+ "(app_id, tenant_id, user_id, email)" + " VALUES(?, ?, ?, ?)";

List<PreparedStatementValueSetter> recipeUserAccountInfoBatch = new ArrayList<>();
List<PreparedStatementValueSetter> recipeUserTenantsBatch = new ArrayList<>();

List<PreparedStatementValueSetter> appIdToUserIdSetters = new ArrayList<>();
List<PreparedStatementValueSetter> allAuthRecipeUsersSetters = new ArrayList<>();
List<PreparedStatementValueSetter> emailPasswordUsersSetters = new ArrayList<>();
List<PreparedStatementValueSetter> emailPasswordUsersToTenantSetters = new ArrayList<>();

for (EmailPasswordImportUser user : usersToSignUp) {
String userId = user.userId;
TenantIdentifier tenantIdentifier = user.tenantIdentifier;
String appId = user.appIdentifier.getAppId();
String primaryOrRecipeUserId = user.primaryUserId != null ? user.primaryUserId : user.userId;
boolean isLinkedOrIsPrimaryUser = user.primaryUserId != null;

// Recipe Account Info
AccountInfoQueries.addRecipeUserAccountInfoToBatch(recipeUserAccountInfoBatch, user.appIdentifier, user.userId, EMAIL_PASSWORD.toString(), ACCOUNT_INFO_TYPE.EMAIL, "", "", user.email, isLinkedOrIsPrimaryUser ? primaryOrRecipeUserId : null);

// Recipe User Tenants
AccountInfoQueries.addRecipeUserTenantsToBatch(recipeUserTenantsBatch, user.appIdentifier, user.userId, EMAIL_PASSWORD.toString(), ACCOUNT_INFO_TYPE.EMAIL, "", "", user.email, user.recipeUserTenantIds);


appIdToUserIdSetters.add(pst -> {
pst.setString(1, tenantIdentifier.getAppId());
pst.setString(1, appId);
pst.setString(2, userId);
pst.setString(3, userId);
pst.setString(4, EMAIL_PASSWORD.toString());
});

allAuthRecipeUsersSetters.add(pst -> {
pst.setString(1, tenantIdentifier.getAppId());
pst.setString(2, tenantIdentifier.getTenantId());
pst.setString(3, userId);
pst.setString(4, userId);
pst.setString(3, primaryOrRecipeUserId);
pst.setBoolean(4, isLinkedOrIsPrimaryUser);
pst.setString(5, EMAIL_PASSWORD.toString());
pst.setLong(6, user.timeJoinedMSSinceEpoch);
pst.setLong(7, user.timeJoinedMSSinceEpoch);
});

emailPasswordUsersSetters.add(pst -> {
pst.setString(1, tenantIdentifier.getAppId());
pst.setString(1, appId);
pst.setString(2, userId);
pst.setString(3, user.email);
pst.setString(4, user.passwordHash);
pst.setLong(5, user.timeJoinedMSSinceEpoch);
});

emailPasswordUsersToTenantSetters.add(pst -> {
pst.setString(1, tenantIdentifier.getAppId());
pst.setString(2, tenantIdentifier.getTenantId());
pst.setString(3, userId);
pst.setString(4, user.email);
});
// Generate entries for all recipe user tenant IDs
for (String tenantId : user.recipeUserTenantIds) {
allAuthRecipeUsersSetters.add(pst -> {
pst.setString(1, appId);
pst.setString(2, tenantId);
pst.setString(3, userId);
pst.setString(4, primaryOrRecipeUserId);
pst.setBoolean(5, isLinkedOrIsPrimaryUser);
pst.setString(6, EMAIL_PASSWORD.toString());
pst.setLong(7, user.timeJoinedMSSinceEpoch);
pst.setLong(8, user.timeJoinedMSSinceEpoch);
});

emailPasswordUsersToTenantSetters.add(pst -> {
pst.setString(1, appId);
pst.setString(2, tenantId);
pst.setString(3, userId);
pst.setString(4, user.email);
});
}
}

executeBatch(sqlCon, AccountInfoQueries.getRecipeUserAccountInfoBatchQuery(start), recipeUserAccountInfoBatch);
executeBatch(sqlCon, AccountInfoQueries.getRecipeUserTenantBatchQuery(start), recipeUserTenantsBatch);

executeBatch(sqlCon, app_id_to_user_id_QUERY, appIdToUserIdSetters);
executeBatch(sqlCon, all_auth_recipe_users_QUERY, allAuthRecipeUsersSetters);
executeBatch(sqlCon, emailpassword_users_QUERY, emailPasswordUsersSetters);
Expand Down
Loading