[GH-ISSUE #821] Attributes are not sorted, users are not sorted, or previous user didn't consume all the attributes #294

Closed
opened 2026-02-27 08:16:26 +03:00 by kerem · 21 comments
Owner

Originally created by @dvv on GitHub (Jan 29, 2024).
Original GitHub issue: https://github.com/lldap/lldap/issues/821

Describe the bug

Panic "Attributes are not sorted, users are not sorted, or previous user didn't consume all the attributes" after migration.

To Reproduce
Steps to reproduce the behavior:

  1. Migrate from another LDAP via the migration tool
  2. Restart the server
  3. Login
  4. See error

Expected behavior

Server just works.
Is there a way to repack the database (postgresql) to fix the problem?

Originally created by @dvv on GitHub (Jan 29, 2024). Original GitHub issue: https://github.com/lldap/lldap/issues/821 **Describe the bug** Panic "Attributes are not sorted, users are not sorted, or previous user didn't consume all the attributes" after migration. **To Reproduce** Steps to reproduce the behavior: 1. Migrate from another LDAP via the migration tool 2. Restart the server 3. Login 4. See error **Expected behavior** Server just works. Is there a way to repack the database (postgresql) to fix the problem?
kerem 2026-02-27 08:16:26 +03:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@nitnelave commented on GitHub (Jan 30, 2024):

Hmm, I can't reproduce the panic. Could you post a sql dump of the DB, or send it to me privately on Discord? Don't worry, I won't be able to get the passwords.

<!-- gh-comment-id:1917444414 --> @nitnelave commented on GitHub (Jan 30, 2024): Hmm, I can't reproduce the panic. Could you post a sql dump of the DB, or send it to me privately on Discord? Don't worry, I won't be able to get the passwords.
Author
Owner

@dvv commented on GitHub (Jan 30, 2024):

@nitnelave sorry, no, I don't own it.

Indeed I saw no sorting in migration tool hence user ids come assorted from the source LDAP.
At first I tried to sort them:

--- a/migration-tool/src/main.rs                                                                                                                                      
+++ b/migration-tool/src/main.rs
@@ -23,11 +23,12 @@ fn ask_generic_confirmation(name: &str, message: &str) -> Result<bool> {
 fn get_users_to_add(users: &[User], existing_users: &[String]) -> Result<Option<Vec<User>>> {
     let existing_users = HashSet::<&String>::from_iter(existing_users);
     let num_found_users = users.len();
-    let input_users: Vec<_> = users
+    let mut input_users: Vec<_> = users
         .iter()
         .filter(|u| !existing_users.contains(&u.user_input.id))
         .map(User::clone)
         .collect();
+    input_users.sort_by(|u1, u2| u1.user_input.id.cmp(&u2.user_input.id));
     println!(
         "Found {} users, of which {} new users: [\n  {}\n]",
         num_found_users,

but the error persisted.

Then I commented out the assertion:

--- a/server/src/domain/sql_user_backend_handler.rs
+++ b/server/src/domain/sql_user_backend_handler.rs
@@ -145,11 +145,13 @@ impl UserListerBackendHandler for SqlBackendHandler {
         let mut attributes_iter = attributes.into_iter().peekable();
         use itertools::Itertools; // For take_while_ref
         for user in users.iter_mut() {
+            /*
             assert!(attributes_iter
                 .peek()
                 .map(|u| u.user_id >= user.user.user_id)
                 .unwrap_or(true),
                 "Attributes are not sorted, users are not sorted, or previous user didn't consume all the attributes");
+            */

             user.user.attributes = attributes_iter
                 .take_while_ref(|u| u.user_id == user.user.user_id)

and it worked.

Still I can not use it: I cured symptom but indeed I broke the logic. And I do not understand the logic.

<!-- gh-comment-id:1917477044 --> @dvv commented on GitHub (Jan 30, 2024): @nitnelave sorry, no, I don't own it. Indeed I saw no sorting in migration tool hence user ids come assorted from the source LDAP. At first I tried to sort them: ```diff --- a/migration-tool/src/main.rs +++ b/migration-tool/src/main.rs @@ -23,11 +23,12 @@ fn ask_generic_confirmation(name: &str, message: &str) -> Result<bool> { fn get_users_to_add(users: &[User], existing_users: &[String]) -> Result<Option<Vec<User>>> { let existing_users = HashSet::<&String>::from_iter(existing_users); let num_found_users = users.len(); - let input_users: Vec<_> = users + let mut input_users: Vec<_> = users .iter() .filter(|u| !existing_users.contains(&u.user_input.id)) .map(User::clone) .collect(); + input_users.sort_by(|u1, u2| u1.user_input.id.cmp(&u2.user_input.id)); println!( "Found {} users, of which {} new users: [\n {}\n]", num_found_users, ``` but the error persisted. Then I commented out the assertion: ```diff --- a/server/src/domain/sql_user_backend_handler.rs +++ b/server/src/domain/sql_user_backend_handler.rs @@ -145,11 +145,13 @@ impl UserListerBackendHandler for SqlBackendHandler { let mut attributes_iter = attributes.into_iter().peekable(); use itertools::Itertools; // For take_while_ref for user in users.iter_mut() { + /* assert!(attributes_iter .peek() .map(|u| u.user_id >= user.user.user_id) .unwrap_or(true), "Attributes are not sorted, users are not sorted, or previous user didn't consume all the attributes"); + */ user.user.attributes = attributes_iter .take_while_ref(|u| u.user_id == user.user.user_id) ``` and it worked. Still I can not use it: I cured symptom but indeed I broke the logic. And I do not understand the logic.
Author
Owner

@nitnelave commented on GitHub (Jan 30, 2024):

The ordering comes from the SQL query, not the migration: the query is ordered by user id, and so are the attributes. Is there a debug mode in PostgreSQL that prints out the queries? Otherwise, you can run LLDAP with RUST_LOG=debug, I think that should print a lot of things, including the SQL queries used.

As for the logic, we're iterating over the users and the attributes vectors, associating every run of attributes with the same user id with the corresponding user. That relies on both users and attributes to be sorted by user id.

<!-- gh-comment-id:1917609871 --> @nitnelave commented on GitHub (Jan 30, 2024): The ordering comes from the SQL query, not the migration: the query is ordered by user id, and so are the attributes. Is there a debug mode in PostgreSQL that prints out the queries? Otherwise, you can run LLDAP with RUST_LOG=debug, I think that should print a lot of things, including the SQL queries used. As for the logic, we're iterating over the users and the attributes vectors, associating every run of attributes with the same user id with the corresponding user. That relies on both users and attributes to be sorted by user id.
Author
Owner

@nitnelave commented on GitHub (Jan 30, 2024):

Just to confirm, you used the migration tool provided by LLDAP, correct? Can you confirm that all the user IDs are lowercase in the DB? Something like "SELECT user_id FROM users;" (or select I'd maybe)

<!-- gh-comment-id:1917614659 --> @nitnelave commented on GitHub (Jan 30, 2024): Just to confirm, you used the migration tool provided by LLDAP, correct? Can you confirm that all the user IDs are lowercase in the DB? Something like "SELECT user_id FROM users;" (or select I'd maybe)
Author
Owner

@dvv commented on GitHub (Jan 30, 2024):

Yes, exactly.

Lowercased:

lldap=# select user_id from users where user_id != lower(user_id);
 user_id
---------
(0 rows)
<!-- gh-comment-id:1917630216 --> @dvv commented on GitHub (Jan 30, 2024): Yes, exactly. Lowercased: ```sql lldap=# select user_id from users where user_id != lower(user_id); user_id --------- (0 rows) ```
Author
Owner

@dvv commented on GitHub (Jan 30, 2024):

I see when listing users in web ui:

auth-lldap-1  | 2024-01-30T18:53:58.923828459+00:00  INFO     LDAP session [ 84.5µs | 36.77% / 100.00% ]
auth-lldap-1  | 2024-01-30T18:53:58.923836389+00:00  INFO     ┕━ LDAP request [ 53.4µs | 63.23% ]
auth-lldap-1  | 2024-01-30T18:53:58.923842956+00:00  DEBUG       ┝━ 🐛 [debug]:  | msg: LdapMsg { msgid: 0, op: SearchRequest(LdapSearchRequest { base: "", scope: Base, alias
es: Never, sizelimit: 0, timelimit: 0, typesonly: false, filter: Present("objectClass"), attrs: ["supportedExtension"] }), ctrl: [] }
auth-lldap-1  | 2024-01-30T18:53:58.923844536+00:00  DEBUG       ┝━ 🐛 [debug]: rootDSE request
auth-lldap-1  | 2024-01-30T18:53:58.923857301+00:00  DEBUG       ┝━ 🐛 [debug]:  | response: SearchResultEntry(LdapSearchResultEntry { dn: "", attributes: [LdapPartialAttribu
te { atype: "objectClass", vals: ["top"] }, LdapPartialAttribute { atype: "vendorName", vals: ["LLDAP"] }, LdapPartialAttribute { atype: "vendorVersion", vals: ["lldap_0.5.1-
alpha"] }, LdapPartialAttribute { atype: "supportedLDAPVersion", vals: ["3"] }, LdapPartialAttribute { atype: "supportedExtension", vals: ["1.3.6.1.4.1.4203.1.11.1"] }, LdapP
artialAttribute { atype: "supportedControl", vals: [] }, LdapPartialAttribute { atype: "supportedFeatures", vals: ["1.3.6.1.4.1.4203.1.5.1"] }, LdapPartialAttribute { atype:
"defaultNamingContext", vals: ["dc=example,dc=com"] }, LdapPartialAttribute { atype: "namingContexts", vals: ["dc=example,dc=com"] }, LdapPartialAttribute { atype: "isGlobalCat
alogReady", vals: ["false"] }] })
auth-lldap-1  | 2024-01-30T18:53:58.923879452+00:00  DEBUG       ┕━ 🐛 [debug]:  | response: SearchResultDone(LdapResult { code: Success, matcheddn: "", message: "", referral
: [] })
auth-lldap-1  | 2024-01-30T18:54:00.764995685+00:00  DEBUG    check_if_token_is_valid [ 81.0µs | 100.00% ]
auth-lldap-1  | 2024-01-30T18:54:00.765075553+00:00  DEBUG    ┕━ 🐛 [debug]:  | return: ValidationResults { user: UserId(CaseInsensitiveString("admin")), permission: Admin }
auth-lldap-1  | thread 'actix-rt|system:0|arbiter:0' panicked at server/src/domain/sql_user_backend_handler.rs:148:13:
auth-lldap-1  | Attributes are not sorted, users are not sorted, or previous user didn't consume all the attributes
auth-lldap-1  | stack backtrace:
auth-lldap-1  |    0: rust_begin_unwind
auth-lldap-1  |              at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/std/src/panicking.rs:597:5
auth-lldap-1  |    1: core::panicking::panic_fmt
auth-lldap-1  |              at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/panicking.rs:72:14
auth-lldap-1  |    2: lldap::domain::sql_user_backend_handler::<impl lldap::domain::handler::UserListerBackendHandler for lldap::domain::sql_backend_handler::SqlBackendHandle
r>::list_users::{{closure}}
auth-lldap-1  |    3: <Handler as lldap::infra::access_control::ReadonlyBackendHandler>::list_users::{{closure}}
auth-lldap-1  |    4: <tracing::instrument::Instrumented<T> as core::future::future::Future>::poll
auth-lldap-1  |    5: <lldap::infra::graphql::query::Query<Handler> as juniper::types::async_await::GraphQLValueAsync<__S>>::resolve_field_async::{{closure}}
auth-lldap-1  |    6: juniper::types::async_await::resolve_selection_set_into_async_recursive::{{closure}}
auth-lldap-1  |    7: juniper::types::async_await::GraphQLValueAsync::resolve_async::{{closure}}
auth-lldap-1  |    8: juniper::http::GraphQLRequest<S>::execute::{{closure}}.34508
auth-lldap-1  |    9: actix_web::handler::handler_service::{{closure}}::{{closure}}
auth-lldap-1  |   10: <actix_web::resource::Resource<T> as actix_web::service::HttpServiceFactory>::register::{{closure}}::{{closure}}
auth-lldap-1  |   11: <core::pin::Pin<P> as core::future::future::Future>::poll
auth-lldap-1  |   12: <actix_web::scope::Scope<T> as actix_web::service::HttpServiceFactory>::register::{{closure}}::{{closure}}
auth-lldap-1  |   13: <actix_web::middleware::condition::ConditionMiddlewareFuture<E,D> as core::future::future::Future>::poll
auth-lldap-1  |   14: actix_http::h1::dispatcher::InnerDispatcher<T,S,B,X,U>::poll_response
auth-lldap-1  |   15: <actix_http::h1::dispatcher::Dispatcher<T,S,B,X,U> as core::future::future::Future>::poll
auth-lldap-1  |   16: tokio::runtime::task::raw::poll
auth-lldap-1  |   17: tokio::task::local::LocalSet::tick
auth-lldap-1  |   18: <tokio::task::local::RunUntil<T> as core::future::future::Future>::poll
auth-lldap-1  |   19: tokio::task::local::LocalSet::run_until::{{closure}}
auth-lldap-1  | note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
auth-lldap-1  | 2024-01-30T18:54:00.767786062+00:00  DEBUG    [GraphQL query] users [ 184ms | 1.79% / 100.00% ]
auth-lldap-1  | 2024-01-30T18:54:00.767794830+00:00  DEBUG    ┝━ 🐛 [debug]:  | filters: None
auth-lldap-1  | 2024-01-30T18:54:00.769259284+00:00  DEBUG    ┝━ 🐛 [debug]:  | summary: "BEGIN" | db.statement: "" | rows_affected: 0 | rows_returned: 0 | elapsed: 647.479µs
auth-lldap-1  | 2024-01-30T18:54:00.772258135+00:00  DEBUG    ┝━ 🐛 [debug]:  | summary: "SELECT \"user_attribute_schema\".\"user_attribute_schema_name\", \"user_attribute_sc
hema\".\"user_attribute_schema_type\", \"user_attribute_schema\".\"user_attribute_schema_is_list\", …" | db.statement: "\n\nSELECT\n  \"user_attribute_schema\".\"user_attribu
te_schema_name\",\n  \"user_attribute_schema\".\"user_attribute_schema_type\",\n  \"user_attribute_schema\".\"user_attribute_schema_is_list\",\n  \"user_attribute_schema\".\"
user_attribute_schema_is_user_visible\",\n  \"user_attribute_schema\".\"user_attribute_schema_is_user_editable\",\n  \"user_attribute_schema\".\"user_attribute_schema_is_hard
coded\"\nFROM\n  \"user_attribute_schema\"\nORDER BY\n  \"user_attribute_schema\".\"user_attribute_schema_name\" ASC\n" | rows_affected: 3 | rows_returned: 3 | elapsed: 2.268
182ms
auth-lldap-1  | 2024-01-30T18:54:00.775296545+00:00  DEBUG    ┝━ 🐛 [debug]:  | summary: "SELECT \"group_attribute_schema\".\"group_attribute_schema_name\", \"group_attribute
_schema\".\"group_attribute_schema_type\", \"group_attribute_schema\".\"group_attribute_schema_is_list\", …" | db.statement: "\n\nSELECT\n  \"group_attribute_schema\".\"group
_attribute_schema_name\",\n  \"group_attribute_schema\".\"group_attribute_schema_type\",\n  \"group_attribute_schema\".\"group_attribute_schema_is_list\",\n  \"group_attribut
e_schema\".\"group_attribute_schema_is_group_visible\",\n  \"group_attribute_schema\".\"group_attribute_schema_is_group_editable\",\n  \"group_attribute_schema\".\"group_attr
ibute_schema_is_hardcoded\"\nFROM\n  \"group_attribute_schema\"\nORDER BY\n  \"group_attribute_schema\".\"group_attribute_schema_name\" ASC\n" | rows_affected: 0 | rows_retur
ned: 0 | elapsed: 2.237188ms
auth-lldap-1  | 2024-01-30T18:54:00.776090725+00:00  DEBUG    ┝━ 🐛 [debug]:  | summary: "COMMIT" | db.statement: "" | rows_affected: 0 | rows_returned: 0 | elapsed: 742.46µs
auth-lldap-1  | 2024-01-30T18:54:00.776229233+00:00  DEBUG    ┕━ list_users [ 181ms | 98.21% ] filters: None | _get_groups: false
auth-lldap-1  | 2024-01-30T18:54:00.807388036+00:00  DEBUG       ┝━ 🐛 [debug]:  | summary: "SELECT \"users\".\"user_id\" AS \"A_user_id\", …" | db.statement: "\n\nSELECT\n
\"users\".\"user_id\" AS \"A_user_id\",\n  \"users\".\"email\" AS \"A_email\",\n  \"users\".\"lowercase_email\" AS \"A_lowercase_email\",\n  \"users\".\"display_name\" AS \"A
_display_name\",\n  \"users\".\"creation_date\" AS \"A_creation_date\",\n  \"users\".\"password_hash\" AS \"A_password_hash\",\n  \"users\".\"totp_secret\" AS \"A_totp_secret
\",\n  \"users\".\"mfa_type\" AS \"A_mfa_type\",\n  \"users\".\"uuid\" AS \"A_uuid\",\n  \"r1\".\"group_id\" AS \"B_group_id\",\n  \"r1\".\"display_name\" AS \"B_display_name
\",\n  \"r1\".\"lowercase_display_name\" AS \"B_lowercase_display_name\",\n  \"r1\".\"creation_date\" AS \"B_creation_date\",\n  \"r1\".\"uuid\" AS \"B_uuid\"\nFROM\n  \"user
s\"\n  LEFT JOIN \"memberships\" AS \"r0\" ON \"users\".\"user_id\" = \"r0\".\"user_id\"\n  LEFT JOIN \"groups\" AS \"r1\" ON \"r0\".\"group_id\" = \"r1\".\"group_id\"\nWHERE
\n  $1\nORDER BY\n  \"users\".\"user_id\" ASC,\n  \"r1\".\"display_name\" ASC\n" | rows_affected: 1246 | rows_returned: 1246 | elapsed: 29.205577ms
auth-lldap-1  | 2024-01-30T18:54:00.845373003+00:00  DEBUG       ┕━ 🐛 [debug]:  | summary: "SELECT \"user_attributes\".\"user_attribute_user_id\", \"user_attributes\".\"user
_attribute_name\", \"user_attributes\".\"user_attribute_value\" …" | db.statement: "\n\nSELECT\n  \"user_attributes\".\"user_attribute_user_id\",\n  \"user_attributes\".\"use
r_attribute_name\",\n  \"user_attributes\".\"user_attribute_value\"\nFROM\n  \"user_attributes\"\nWHERE\n  \"user_attributes\".\"user_attribute_user_id\" IN (\n    $1,\n    $
2,\n    $3,\n    $4,\n    $5,\n    $6,\n    $7,\n    $8,\n    $9,\n    $10,\n    $11,\n    $12,\n    $13,\n    $14,\n    $15,\n    $16,\n    $17,\n    $18,\n    $19,\n    $20
,\n    $21,\n    $22,\n    $23,\n    $24,\n    $25,\n    $26,\n    $27,\n    $28,\n    $29,\n    $30,\n    $31,\n    $32,\n    $33,\n    $34,\n    $35,\n    $36,\n    $37,\n
   $38,\n    $39,\n    $40,\n    $41,\n    $42,\n    $43,\n    $44,\n    $45,\n    $46,\n    $47,\n    $48,\n    $49,\n    $50,\n    $51,\n    $52,\n    $53,\n    $54,\n    $
55,\n    $56,\n    $57,\n    $58,\n    $59,\n    $60,\n    $61,\n    $62,\n    $63,\n    $64,\n    $65,\n    $66,\n    $67,\n    $68,\n    $69,\n    $70,\n    $71,\n    $72,\
n    $73,\n    $74,\n    $75,\n    $76,\n    $77,\n    $78,\n    $79,\n    $80,\n    $81,\n    $82,\n    $83,\n    $84,\n    $85,\n    $86,\n    $87,\n    $88,\n    $89,\n
 $90,\n    $91,\n    $92,\n    $93,\n    $94,\n    $95,\n    $96,\n    $97,\n    $98,\n    $99,\n    $100,\n    $101,\n    $102,\n    $103,\n    $104,\n    $105,\n    $106,\n
    $107,\n    $108,\n    $109,\n    $110,\n    $111,\n    $112,\n    $113,\n    $114,\n    $115,\n    $116,\n    $117,\n    $118,\n    $119,\n    $120,\n    $121,\n    $122,
\n    $123,\n    $124,\n    $125,\n    $126,\n    $127,\n    $128,\n    $129,\n    $130,\n    $131,\n    $132,\n    $133,\n    $134,\n    $135,\n    $136,\n    $137,\n    $13
8,\n    $139,\n    $140,\n    $141,\n    $142,\n    $143,\n    $144,\n    $145,\n    $146,\n    $147,\n    $148,\n    $149,\n    $150,\n    $151,\n    $152,\n    $153,\n    $
154,\n    $155,\n    $156,\n    $157,\n    $158,\n    $159,\n    $160,\n    $161,\n    $162,\n    $163,\n    $164,\n    $165,\n    $166,\n    $167,\n    $168,\n    $169,\n
 $170,\n    $171,\n    $172,\n    $173,\n    $174,\n    $175,\n    $176,\n    $177,\n    $178,\n    $179,\n    $180,\n    $181,\n    $182,\n    $183,\n    $184,\n    $185,\n
   $186,\n    $187,\n    $188,\n    $189,\n    $190,\n    $191,\n    $192,\n    $193,\n    $194,\n    $195,\n    $196,\n    $197,\n    $198,\n    $199,\n    $200,\n    $201,\
n    $202,\n    $203,\n    $204,\n    $205,\n    $206,\n    $207,\n    $208,\n    $209,\n    $210,\n    $211,\n    $212,\n    $213,\n    $214,\n    $215,\n    $216,\n    $217
,\n    $218,\n    $219,\n    $220,\n    $221,\n    $222,\n    $223,\n    $224,\n    $225,\n    $226,\n    $227,\n    $228,\n    $229,\n    $230,\n    $231,\n    $232,\n    $2
33,\n    $234,\n    $235,\n    $236,\n    $237,\n    $238,\n    $239,\n    $240,\n    $241,\n    $242,\n    $243,\n    $244,\n    $245,\n    $246,\n    $247,\n    $248,\n
$249,\n    $250,\n    $251,\n    $252,\n    $253,\n    $254,\n    $255,\n    $256,\n    $257,\n    $258,\n    $259,\n    $260,\n    $261,\n    $262,\n    $263,\n    $264,\n
  $265,\n    $266,\n    $267,\n    $268,\n    $269,\n    $270,\n    $271,\n    $272,\n    $273,\n    $274,\n    $275,\n    $276,\n    $277,\n    $278,\n    $279,\n    $280,\n
    $281,\n    $282,\n    $283,\n    $284,\n    $285,\n    $286,\n    $287,\n    $288,\n    $289,\n    $290,\n    $291,\n    $292,\n    $293,\n    $294,\n    $295,\n    $296,
\n    $297,\n    $298,\n    $299,\n    $300,\n    $301,\n    $302,\n    $303,\n    $304,\n    $305,\n    $306,\n    $307,\n    $308,\n    $309,\n    $310,\n    $311,\n    $31
2,\n    $313,\n    $314,\n    $315,\n    $316,\n    $317,\n    $318,\n    $319,\n    $320,\n    $321,\n    $322,\n    $323,\n    $324,\n    $325,\n    $326,\n    $327,\n    $
328,\n    $329,\n    $330,\n    $331,\n    $332,\n    $333,\n    $334,\n    $335,\n    $336,\n    $337,\n    $338,\n    $339,\n    $340,\n    $341,\n    $342,\n    $343,\n
 $344,\n    $345,\n    $346,\n    $347,\n    $348,\n    $349,\n    $350,\n    $351,\n    $352,\n    $353,\n    $354,\n    $355,\n    $356,\n    $357,\n    $358,\n    $359,\n
   $360,\n    $361,\n    $362,\n    $363,\n    $364,\n    $365,\n    $366,\n    $367,\n    $368,\n    $369,\n    $370,\n    $371,\n    $372,\n    $373,\n    $374,\n    $375,\
n    $376,\n    $377,\n    $378,\n    $379,\n    $380,\n    $381,\n    $382,\n    $383,\n    $384,\n    $385,\n    $386,\n    $387,\n    $388,\n    $389,\n    $390,\n    $391
,\n    $392,\n    $393,\n    $394,\n    $395,\n    $396,\n    $397,\n    $398,\n    $399,\n    $400,\n    $401,\n    $402,\n    $403,\n    $404,\n    $405,\n    $406,\n    $4
07,\n    $408,\n    $409,\n    $410,\n    $411,\n    $412,\n    $413,\n    $414,\n    $415,\n    $416,\n    $417,\n    $418,\n    $419,\n    $420,\n    $421,\n    $422,\n
$423,\n    $424,\n    $425,\n    $426,\n    $427,\n    $428,\n    $429,\n    $430,\n    $431,\n    $432,\n    $433,\n    $434,\n    $435,\n    $436,\n    $437,\n    $438,\n
  $439,\n    $440,\n    $441,\n    $442,\n    $443,\n    $444,\n    $445,\n    $446,\n    $447,\n    $448,\n    $449,\n    $450,\n    $451,\n    $452,\n    $453,\n    $454,\n
    $455,\n    $456,\n    $457,\n    $458,\n    $459,\n    $460,\n    $461,\n    $462,\n    $463,\n    $464,\n    $465,\n    $466,\n    $467,\n    $468,\n    $469,\n    $470,
\n    $471,\n    $472,\n    $473,\n    $474,\n    $475,\n    $476\n  )\nORDER BY\n  \"user_attributes\".\"user_attribute_user_id\" ASC,\n  \"user_attributes\".\"user_attribu........

The only quirk I see is an awful monstrous ... IN (...) clause. But I can not find the query source. Does it come from some ORM? Hmm. What if I'd have much more than 476 users?
IMO such queries should read select ... from ... where foo = any($1) passing the array to the parameter.

<!-- gh-comment-id:1917724650 --> @dvv commented on GitHub (Jan 30, 2024): I see when listing users in web ui: ```log auth-lldap-1 | 2024-01-30T18:53:58.923828459+00:00 INFO LDAP session [ 84.5µs | 36.77% / 100.00% ] auth-lldap-1 | 2024-01-30T18:53:58.923836389+00:00 INFO ┕━ LDAP request [ 53.4µs | 63.23% ] auth-lldap-1 | 2024-01-30T18:53:58.923842956+00:00 DEBUG ┝━ 🐛 [debug]: | msg: LdapMsg { msgid: 0, op: SearchRequest(LdapSearchRequest { base: "", scope: Base, alias es: Never, sizelimit: 0, timelimit: 0, typesonly: false, filter: Present("objectClass"), attrs: ["supportedExtension"] }), ctrl: [] } auth-lldap-1 | 2024-01-30T18:53:58.923844536+00:00 DEBUG ┝━ 🐛 [debug]: rootDSE request auth-lldap-1 | 2024-01-30T18:53:58.923857301+00:00 DEBUG ┝━ 🐛 [debug]: | response: SearchResultEntry(LdapSearchResultEntry { dn: "", attributes: [LdapPartialAttribu te { atype: "objectClass", vals: ["top"] }, LdapPartialAttribute { atype: "vendorName", vals: ["LLDAP"] }, LdapPartialAttribute { atype: "vendorVersion", vals: ["lldap_0.5.1- alpha"] }, LdapPartialAttribute { atype: "supportedLDAPVersion", vals: ["3"] }, LdapPartialAttribute { atype: "supportedExtension", vals: ["1.3.6.1.4.1.4203.1.11.1"] }, LdapP artialAttribute { atype: "supportedControl", vals: [] }, LdapPartialAttribute { atype: "supportedFeatures", vals: ["1.3.6.1.4.1.4203.1.5.1"] }, LdapPartialAttribute { atype: "defaultNamingContext", vals: ["dc=example,dc=com"] }, LdapPartialAttribute { atype: "namingContexts", vals: ["dc=example,dc=com"] }, LdapPartialAttribute { atype: "isGlobalCat alogReady", vals: ["false"] }] }) auth-lldap-1 | 2024-01-30T18:53:58.923879452+00:00 DEBUG ┕━ 🐛 [debug]: | response: SearchResultDone(LdapResult { code: Success, matcheddn: "", message: "", referral : [] }) auth-lldap-1 | 2024-01-30T18:54:00.764995685+00:00 DEBUG check_if_token_is_valid [ 81.0µs | 100.00% ] auth-lldap-1 | 2024-01-30T18:54:00.765075553+00:00 DEBUG ┕━ 🐛 [debug]: | return: ValidationResults { user: UserId(CaseInsensitiveString("admin")), permission: Admin } auth-lldap-1 | thread 'actix-rt|system:0|arbiter:0' panicked at server/src/domain/sql_user_backend_handler.rs:148:13: auth-lldap-1 | Attributes are not sorted, users are not sorted, or previous user didn't consume all the attributes auth-lldap-1 | stack backtrace: auth-lldap-1 | 0: rust_begin_unwind auth-lldap-1 | at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/std/src/panicking.rs:597:5 auth-lldap-1 | 1: core::panicking::panic_fmt auth-lldap-1 | at /rustc/a28077b28a02b92985b3a3faecf92813155f1ea1/library/core/src/panicking.rs:72:14 auth-lldap-1 | 2: lldap::domain::sql_user_backend_handler::<impl lldap::domain::handler::UserListerBackendHandler for lldap::domain::sql_backend_handler::SqlBackendHandle r>::list_users::{{closure}} auth-lldap-1 | 3: <Handler as lldap::infra::access_control::ReadonlyBackendHandler>::list_users::{{closure}} auth-lldap-1 | 4: <tracing::instrument::Instrumented<T> as core::future::future::Future>::poll auth-lldap-1 | 5: <lldap::infra::graphql::query::Query<Handler> as juniper::types::async_await::GraphQLValueAsync<__S>>::resolve_field_async::{{closure}} auth-lldap-1 | 6: juniper::types::async_await::resolve_selection_set_into_async_recursive::{{closure}} auth-lldap-1 | 7: juniper::types::async_await::GraphQLValueAsync::resolve_async::{{closure}} auth-lldap-1 | 8: juniper::http::GraphQLRequest<S>::execute::{{closure}}.34508 auth-lldap-1 | 9: actix_web::handler::handler_service::{{closure}}::{{closure}} auth-lldap-1 | 10: <actix_web::resource::Resource<T> as actix_web::service::HttpServiceFactory>::register::{{closure}}::{{closure}} auth-lldap-1 | 11: <core::pin::Pin<P> as core::future::future::Future>::poll auth-lldap-1 | 12: <actix_web::scope::Scope<T> as actix_web::service::HttpServiceFactory>::register::{{closure}}::{{closure}} auth-lldap-1 | 13: <actix_web::middleware::condition::ConditionMiddlewareFuture<E,D> as core::future::future::Future>::poll auth-lldap-1 | 14: actix_http::h1::dispatcher::InnerDispatcher<T,S,B,X,U>::poll_response auth-lldap-1 | 15: <actix_http::h1::dispatcher::Dispatcher<T,S,B,X,U> as core::future::future::Future>::poll auth-lldap-1 | 16: tokio::runtime::task::raw::poll auth-lldap-1 | 17: tokio::task::local::LocalSet::tick auth-lldap-1 | 18: <tokio::task::local::RunUntil<T> as core::future::future::Future>::poll auth-lldap-1 | 19: tokio::task::local::LocalSet::run_until::{{closure}} auth-lldap-1 | note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. auth-lldap-1 | 2024-01-30T18:54:00.767786062+00:00 DEBUG [GraphQL query] users [ 184ms | 1.79% / 100.00% ] auth-lldap-1 | 2024-01-30T18:54:00.767794830+00:00 DEBUG ┝━ 🐛 [debug]: | filters: None auth-lldap-1 | 2024-01-30T18:54:00.769259284+00:00 DEBUG ┝━ 🐛 [debug]: | summary: "BEGIN" | db.statement: "" | rows_affected: 0 | rows_returned: 0 | elapsed: 647.479µs auth-lldap-1 | 2024-01-30T18:54:00.772258135+00:00 DEBUG ┝━ 🐛 [debug]: | summary: "SELECT \"user_attribute_schema\".\"user_attribute_schema_name\", \"user_attribute_sc hema\".\"user_attribute_schema_type\", \"user_attribute_schema\".\"user_attribute_schema_is_list\", …" | db.statement: "\n\nSELECT\n \"user_attribute_schema\".\"user_attribu te_schema_name\",\n \"user_attribute_schema\".\"user_attribute_schema_type\",\n \"user_attribute_schema\".\"user_attribute_schema_is_list\",\n \"user_attribute_schema\".\" user_attribute_schema_is_user_visible\",\n \"user_attribute_schema\".\"user_attribute_schema_is_user_editable\",\n \"user_attribute_schema\".\"user_attribute_schema_is_hard coded\"\nFROM\n \"user_attribute_schema\"\nORDER BY\n \"user_attribute_schema\".\"user_attribute_schema_name\" ASC\n" | rows_affected: 3 | rows_returned: 3 | elapsed: 2.268 182ms auth-lldap-1 | 2024-01-30T18:54:00.775296545+00:00 DEBUG ┝━ 🐛 [debug]: | summary: "SELECT \"group_attribute_schema\".\"group_attribute_schema_name\", \"group_attribute _schema\".\"group_attribute_schema_type\", \"group_attribute_schema\".\"group_attribute_schema_is_list\", …" | db.statement: "\n\nSELECT\n \"group_attribute_schema\".\"group _attribute_schema_name\",\n \"group_attribute_schema\".\"group_attribute_schema_type\",\n \"group_attribute_schema\".\"group_attribute_schema_is_list\",\n \"group_attribut e_schema\".\"group_attribute_schema_is_group_visible\",\n \"group_attribute_schema\".\"group_attribute_schema_is_group_editable\",\n \"group_attribute_schema\".\"group_attr ibute_schema_is_hardcoded\"\nFROM\n \"group_attribute_schema\"\nORDER BY\n \"group_attribute_schema\".\"group_attribute_schema_name\" ASC\n" | rows_affected: 0 | rows_retur ned: 0 | elapsed: 2.237188ms auth-lldap-1 | 2024-01-30T18:54:00.776090725+00:00 DEBUG ┝━ 🐛 [debug]: | summary: "COMMIT" | db.statement: "" | rows_affected: 0 | rows_returned: 0 | elapsed: 742.46µs auth-lldap-1 | 2024-01-30T18:54:00.776229233+00:00 DEBUG ┕━ list_users [ 181ms | 98.21% ] filters: None | _get_groups: false auth-lldap-1 | 2024-01-30T18:54:00.807388036+00:00 DEBUG ┝━ 🐛 [debug]: | summary: "SELECT \"users\".\"user_id\" AS \"A_user_id\", …" | db.statement: "\n\nSELECT\n \"users\".\"user_id\" AS \"A_user_id\",\n \"users\".\"email\" AS \"A_email\",\n \"users\".\"lowercase_email\" AS \"A_lowercase_email\",\n \"users\".\"display_name\" AS \"A _display_name\",\n \"users\".\"creation_date\" AS \"A_creation_date\",\n \"users\".\"password_hash\" AS \"A_password_hash\",\n \"users\".\"totp_secret\" AS \"A_totp_secret \",\n \"users\".\"mfa_type\" AS \"A_mfa_type\",\n \"users\".\"uuid\" AS \"A_uuid\",\n \"r1\".\"group_id\" AS \"B_group_id\",\n \"r1\".\"display_name\" AS \"B_display_name \",\n \"r1\".\"lowercase_display_name\" AS \"B_lowercase_display_name\",\n \"r1\".\"creation_date\" AS \"B_creation_date\",\n \"r1\".\"uuid\" AS \"B_uuid\"\nFROM\n \"user s\"\n LEFT JOIN \"memberships\" AS \"r0\" ON \"users\".\"user_id\" = \"r0\".\"user_id\"\n LEFT JOIN \"groups\" AS \"r1\" ON \"r0\".\"group_id\" = \"r1\".\"group_id\"\nWHERE \n $1\nORDER BY\n \"users\".\"user_id\" ASC,\n \"r1\".\"display_name\" ASC\n" | rows_affected: 1246 | rows_returned: 1246 | elapsed: 29.205577ms auth-lldap-1 | 2024-01-30T18:54:00.845373003+00:00 DEBUG ┕━ 🐛 [debug]: | summary: "SELECT \"user_attributes\".\"user_attribute_user_id\", \"user_attributes\".\"user _attribute_name\", \"user_attributes\".\"user_attribute_value\" …" | db.statement: "\n\nSELECT\n \"user_attributes\".\"user_attribute_user_id\",\n \"user_attributes\".\"use r_attribute_name\",\n \"user_attributes\".\"user_attribute_value\"\nFROM\n \"user_attributes\"\nWHERE\n \"user_attributes\".\"user_attribute_user_id\" IN (\n $1,\n $ 2,\n $3,\n $4,\n $5,\n $6,\n $7,\n $8,\n $9,\n $10,\n $11,\n $12,\n $13,\n $14,\n $15,\n $16,\n $17,\n $18,\n $19,\n $20 ,\n $21,\n $22,\n $23,\n $24,\n $25,\n $26,\n $27,\n $28,\n $29,\n $30,\n $31,\n $32,\n $33,\n $34,\n $35,\n $36,\n $37,\n $38,\n $39,\n $40,\n $41,\n $42,\n $43,\n $44,\n $45,\n $46,\n $47,\n $48,\n $49,\n $50,\n $51,\n $52,\n $53,\n $54,\n $ 55,\n $56,\n $57,\n $58,\n $59,\n $60,\n $61,\n $62,\n $63,\n $64,\n $65,\n $66,\n $67,\n $68,\n $69,\n $70,\n $71,\n $72,\ n $73,\n $74,\n $75,\n $76,\n $77,\n $78,\n $79,\n $80,\n $81,\n $82,\n $83,\n $84,\n $85,\n $86,\n $87,\n $88,\n $89,\n $90,\n $91,\n $92,\n $93,\n $94,\n $95,\n $96,\n $97,\n $98,\n $99,\n $100,\n $101,\n $102,\n $103,\n $104,\n $105,\n $106,\n $107,\n $108,\n $109,\n $110,\n $111,\n $112,\n $113,\n $114,\n $115,\n $116,\n $117,\n $118,\n $119,\n $120,\n $121,\n $122, \n $123,\n $124,\n $125,\n $126,\n $127,\n $128,\n $129,\n $130,\n $131,\n $132,\n $133,\n $134,\n $135,\n $136,\n $137,\n $13 8,\n $139,\n $140,\n $141,\n $142,\n $143,\n $144,\n $145,\n $146,\n $147,\n $148,\n $149,\n $150,\n $151,\n $152,\n $153,\n $ 154,\n $155,\n $156,\n $157,\n $158,\n $159,\n $160,\n $161,\n $162,\n $163,\n $164,\n $165,\n $166,\n $167,\n $168,\n $169,\n $170,\n $171,\n $172,\n $173,\n $174,\n $175,\n $176,\n $177,\n $178,\n $179,\n $180,\n $181,\n $182,\n $183,\n $184,\n $185,\n $186,\n $187,\n $188,\n $189,\n $190,\n $191,\n $192,\n $193,\n $194,\n $195,\n $196,\n $197,\n $198,\n $199,\n $200,\n $201,\ n $202,\n $203,\n $204,\n $205,\n $206,\n $207,\n $208,\n $209,\n $210,\n $211,\n $212,\n $213,\n $214,\n $215,\n $216,\n $217 ,\n $218,\n $219,\n $220,\n $221,\n $222,\n $223,\n $224,\n $225,\n $226,\n $227,\n $228,\n $229,\n $230,\n $231,\n $232,\n $2 33,\n $234,\n $235,\n $236,\n $237,\n $238,\n $239,\n $240,\n $241,\n $242,\n $243,\n $244,\n $245,\n $246,\n $247,\n $248,\n $249,\n $250,\n $251,\n $252,\n $253,\n $254,\n $255,\n $256,\n $257,\n $258,\n $259,\n $260,\n $261,\n $262,\n $263,\n $264,\n $265,\n $266,\n $267,\n $268,\n $269,\n $270,\n $271,\n $272,\n $273,\n $274,\n $275,\n $276,\n $277,\n $278,\n $279,\n $280,\n $281,\n $282,\n $283,\n $284,\n $285,\n $286,\n $287,\n $288,\n $289,\n $290,\n $291,\n $292,\n $293,\n $294,\n $295,\n $296, \n $297,\n $298,\n $299,\n $300,\n $301,\n $302,\n $303,\n $304,\n $305,\n $306,\n $307,\n $308,\n $309,\n $310,\n $311,\n $31 2,\n $313,\n $314,\n $315,\n $316,\n $317,\n $318,\n $319,\n $320,\n $321,\n $322,\n $323,\n $324,\n $325,\n $326,\n $327,\n $ 328,\n $329,\n $330,\n $331,\n $332,\n $333,\n $334,\n $335,\n $336,\n $337,\n $338,\n $339,\n $340,\n $341,\n $342,\n $343,\n $344,\n $345,\n $346,\n $347,\n $348,\n $349,\n $350,\n $351,\n $352,\n $353,\n $354,\n $355,\n $356,\n $357,\n $358,\n $359,\n $360,\n $361,\n $362,\n $363,\n $364,\n $365,\n $366,\n $367,\n $368,\n $369,\n $370,\n $371,\n $372,\n $373,\n $374,\n $375,\ n $376,\n $377,\n $378,\n $379,\n $380,\n $381,\n $382,\n $383,\n $384,\n $385,\n $386,\n $387,\n $388,\n $389,\n $390,\n $391 ,\n $392,\n $393,\n $394,\n $395,\n $396,\n $397,\n $398,\n $399,\n $400,\n $401,\n $402,\n $403,\n $404,\n $405,\n $406,\n $4 07,\n $408,\n $409,\n $410,\n $411,\n $412,\n $413,\n $414,\n $415,\n $416,\n $417,\n $418,\n $419,\n $420,\n $421,\n $422,\n $423,\n $424,\n $425,\n $426,\n $427,\n $428,\n $429,\n $430,\n $431,\n $432,\n $433,\n $434,\n $435,\n $436,\n $437,\n $438,\n $439,\n $440,\n $441,\n $442,\n $443,\n $444,\n $445,\n $446,\n $447,\n $448,\n $449,\n $450,\n $451,\n $452,\n $453,\n $454,\n $455,\n $456,\n $457,\n $458,\n $459,\n $460,\n $461,\n $462,\n $463,\n $464,\n $465,\n $466,\n $467,\n $468,\n $469,\n $470, \n $471,\n $472,\n $473,\n $474,\n $475,\n $476\n )\nORDER BY\n \"user_attributes\".\"user_attribute_user_id\" ASC,\n \"user_attributes\".\"user_attribu........ ``` The only quirk I see is an awful monstrous `... IN (...)` clause. But I can not find the query source. Does it come from some ORM? Hmm. What if I'd have much more than 476 users? IMO such queries should read `select ... from ... where foo = any($1)` passing the array to the parameter.
Author
Owner

@nitnelave commented on GitHub (Jan 30, 2024):

Ah, yeah, LLDAP was not built with "big" servers in mind, more for self-hosted homelabs. But still, 476 users is not that much, it should work.

One limitation that we have is that we support only the subset of SQL common to PG, MySQL and SQLite. That often leads us to write less-than-ideal queries. I'll see if it's possible to pass an array, but I didn't think that an online table would be a problem.

<!-- gh-comment-id:1917796084 --> @nitnelave commented on GitHub (Jan 30, 2024): Ah, yeah, LLDAP was not built with "big" servers in mind, more for self-hosted homelabs. But still, 476 users is not that much, it should work. One limitation that we have is that we support only the subset of SQL common to PG, MySQL and SQLite. That often leads us to write less-than-ideal queries. I'll see if it's possible to pass an array, but I didn't think that an online table would be a problem.
Author
Owner

@dvv commented on GitHub (Jan 30, 2024):

I'm struggling with this:

        let attributes = model::UserAttributes::find()
            //.filter(model::UserAttributesColumn::UserId.is_in(user_ids)) // <-- this may choke
            .filter(model::UserAttributesColumn::UserId.eq(Expr::cust_with_values("ANY(?)", [user_ids]))) <-- this does not compile
            .order_by_asc(model::UserAttributesColumn::UserId)
            .order_by_asc(model::UserAttributesColumn::AttributeName)
            .all(&self.sql_pool)
            .await?;
1.224    Compiling lldap v0.5.1-alpha (/app/server)
4.693 error[E0277]: the trait bound `sea_orm::Value: std::convert::From<std::iter::Map<std::slice::Iter<'_, UserAndGroups>, {closure@server/src/domain/sql_user_backend_handle
r.rs:138:41: 138:44}>>` is not satisfied
4.693    --> server/src/domain/sql_user_backend_handler.rs:141:60
4.693     |
4.693 141 |             .filter(model::UserAttributesColumn::UserId.eq(Expr::cust_with_values("ANY(?)", [user_ids])))
4.693     |                                                            ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<std::iter::Map<std::slice::Iter<'_, UserAndGroups>
, {closure@server/src/domain/sql_user_backend_handler.rs:138:41: 138:44}>>` is not implemented for `sea_orm::Value`
4.693     |
4.693     = help: the following other types implement trait `std::convert::From<T>`:
4.693               <sea_orm::Value as std::convert::From<bool>>
4.693               <sea_orm::Value as std::convert::From<char>>
4.693               <sea_orm::Value as std::convert::From<i8>>
4.693               <sea_orm::Value as std::convert::From<i16>>
4.693               <sea_orm::Value as std::convert::From<i32>>
4.693               <sea_orm::Value as std::convert::From<i64>>
4.693               <sea_orm::Value as std::convert::From<u8>>
4.693               <sea_orm::Value as std::convert::From<u16>>
4.693             and 41 others
4.693     = note: required for `std::iter::Map<std::slice::Iter<'_, UserAndGroups>, {closure@server/src/domain/sql_user_backend_handler.rs:138:41: 138:44}>` to implement `Int
o<sea_orm::Value>`
...

Can you point out what's wrong there?

<!-- gh-comment-id:1917810498 --> @dvv commented on GitHub (Jan 30, 2024): I'm struggling with this: ```rust let attributes = model::UserAttributes::find() //.filter(model::UserAttributesColumn::UserId.is_in(user_ids)) // <-- this may choke .filter(model::UserAttributesColumn::UserId.eq(Expr::cust_with_values("ANY(?)", [user_ids]))) <-- this does not compile .order_by_asc(model::UserAttributesColumn::UserId) .order_by_asc(model::UserAttributesColumn::AttributeName) .all(&self.sql_pool) .await?; ``` ``` 1.224 Compiling lldap v0.5.1-alpha (/app/server) 4.693 error[E0277]: the trait bound `sea_orm::Value: std::convert::From<std::iter::Map<std::slice::Iter<'_, UserAndGroups>, {closure@server/src/domain/sql_user_backend_handle r.rs:138:41: 138:44}>>` is not satisfied 4.693 --> server/src/domain/sql_user_backend_handler.rs:141:60 4.693 | 4.693 141 | .filter(model::UserAttributesColumn::UserId.eq(Expr::cust_with_values("ANY(?)", [user_ids]))) 4.693 | ^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<std::iter::Map<std::slice::Iter<'_, UserAndGroups> , {closure@server/src/domain/sql_user_backend_handler.rs:138:41: 138:44}>>` is not implemented for `sea_orm::Value` 4.693 | 4.693 = help: the following other types implement trait `std::convert::From<T>`: 4.693 <sea_orm::Value as std::convert::From<bool>> 4.693 <sea_orm::Value as std::convert::From<char>> 4.693 <sea_orm::Value as std::convert::From<i8>> 4.693 <sea_orm::Value as std::convert::From<i16>> 4.693 <sea_orm::Value as std::convert::From<i32>> 4.693 <sea_orm::Value as std::convert::From<i64>> 4.693 <sea_orm::Value as std::convert::From<u8>> 4.693 <sea_orm::Value as std::convert::From<u16>> 4.693 and 41 others 4.693 = note: required for `std::iter::Map<std::slice::Iter<'_, UserAndGroups>, {closure@server/src/domain/sql_user_backend_handler.rs:138:41: 138:44}>` to implement `Int o<sea_orm::Value>` ... ``` Can you point out what's wrong there?
Author
Owner

@nitnelave commented on GitHub (Jan 30, 2024):

I'm not in front of the computer, but you can try with user_ids.collect::<Vec<_>>()

<!-- gh-comment-id:1917897222 --> @nitnelave commented on GitHub (Jan 30, 2024): I'm not in front of the computer, but you can try with `user_ids.collect::<Vec<_>>()`
Author
Owner

@nitnelave commented on GitHub (Jan 30, 2024):

Got it to compile with:

        let user_ids: Vec<_> = users.iter().map(|u| u.user.user_id.to_string()).collect();

        let attributes = model::UserAttributes::find()
            //.filter(model::UserAttributesColumn::UserId.eq(Func::cust(Any).arg(user_ids)))
            .filter(Expr::cust_with_exprs(
                "? = ANY(?)",
                [
                    SimpleExpr::Column(model::UserAttributesColumn::UserId.into_column_ref()),
                    user_ids.into(),
                ],
            ))
            .order_by_asc(model::UserAttributesColumn::UserId)
            .order_by_asc(model::UserAttributesColumn::AttributeName)
            .all(&self.sql_pool)
            .await?;

But then we hit Sqlite doesn't support array arguments.

Can you try https://github.com/lldap/lldap/tree/filters instead? I repeat the query above with the same filter as a filter subquery. It's a bit ugly, but it should work (and it passes the tests).

<!-- gh-comment-id:1918101713 --> @nitnelave commented on GitHub (Jan 30, 2024): Got it to compile with: ```rust let user_ids: Vec<_> = users.iter().map(|u| u.user.user_id.to_string()).collect(); let attributes = model::UserAttributes::find() //.filter(model::UserAttributesColumn::UserId.eq(Func::cust(Any).arg(user_ids))) .filter(Expr::cust_with_exprs( "? = ANY(?)", [ SimpleExpr::Column(model::UserAttributesColumn::UserId.into_column_ref()), user_ids.into(), ], )) .order_by_asc(model::UserAttributesColumn::UserId) .order_by_asc(model::UserAttributesColumn::AttributeName) .all(&self.sql_pool) .await?; ``` But then we hit `Sqlite doesn't support array arguments`. Can you try https://github.com/lldap/lldap/tree/filters instead? I repeat the query above with the same filter as a filter subquery. It's a bit ugly, but it should work (and it passes the tests).
Author
Owner

@dvv commented on GitHub (Jan 31, 2024):

"filters" downs at the same assert

<!-- gh-comment-id:1918343646 --> @dvv commented on GitHub (Jan 31, 2024): "filters" downs at the same assert
Author
Owner

@dvv commented on GitHub (Jan 31, 2024):

Well,

-- sqlite3
select 'test.tools' >= 'testsamba'; -- 0
-- mysql
select 'test.tools' >= 'testsamba'; -- 0
# rust
println!(">= {}", "test.tools" >= "testsamba"); // false

but

-- default postgres from docker
select 'test.tools' >= 'testsamba'; -- true
<!-- gh-comment-id:1918355835 --> @dvv commented on GitHub (Jan 31, 2024): Well, ```sql -- sqlite3 select 'test.tools' >= 'testsamba'; -- 0 ``` ```sql -- mysql select 'test.tools' >= 'testsamba'; -- 0 ``` ```rust # rust println!(">= {}", "test.tools" >= "testsamba"); // false ``` but ```sql -- default postgres from docker select 'test.tools' >= 'testsamba'; -- true ```
Author
Owner

@nitnelave commented on GitHub (Jan 31, 2024):

Huh, I didn't think that string comparison would be a sticking point. And
if you remove the assert, are things broken? Both lists should be in the
same order, even if it's not the Rust order.

On Wed, 31 Jan 2024, 05:24 Vladimir Dronnikov, @.***>
wrote:

Well,

-- sqlite3
select 'test.tools' >= 'testsamba'; -- 0

-- mysql
select 'test.tools' >= 'testsamba'; -- 0

rustprintln!(">= {}", "test.tools" >= "testsamba"); // false

but

-- postgres
select 'test.tools' >= 'testsamba'; -- true


Reply to this email directly, view it on GitHub
https://github.com/lldap/lldap/issues/821#issuecomment-1918355835, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AAGCPWLUWMDBBY7VCTDKQVTYRHBPHAVCNFSM6AAAAABCP3HKQSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMJYGM2TKOBTGU
.
You are receiving this because you were mentioned.Message ID:
@.***>

<!-- gh-comment-id:1918444417 --> @nitnelave commented on GitHub (Jan 31, 2024): Huh, I didn't think that string comparison would be a sticking point. And if you remove the assert, are things broken? Both lists should be in the same order, even if it's not the Rust order. On Wed, 31 Jan 2024, 05:24 Vladimir Dronnikov, ***@***.***> wrote: > Well, > > -- sqlite3 > select 'test.tools' >= 'testsamba'; -- 0 > > -- mysql > select 'test.tools' >= 'testsamba'; -- 0 > > # rustprintln!(">= {}", "test.tools" >= "testsamba"); // false > > but > > -- postgres > select 'test.tools' >= 'testsamba'; -- true > > — > Reply to this email directly, view it on GitHub > <https://github.com/lldap/lldap/issues/821#issuecomment-1918355835>, or > unsubscribe > <https://github.com/notifications/unsubscribe-auth/AAGCPWLUWMDBBY7VCTDKQVTYRHBPHAVCNFSM6AAAAABCP3HKQSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMJYGM2TKOBTGU> > . > You are receiving this because you were mentioned.Message ID: > ***@***.***> >
Author
Owner

@nitnelave commented on GitHub (Jan 31, 2024):

Ah, no, itertools is going to break since it'll advance the wrong one...

On Wed, 31 Jan 2024, 07:04 Valentin Tolmer, @.***> wrote:

Huh, I didn't think that string comparison would be a sticking point. And
if you remove the assert, are things broken? Both lists should be in the
same order, even if it's not the Rust order.

On Wed, 31 Jan 2024, 05:24 Vladimir Dronnikov, @.***>
wrote:

Well,

-- sqlite3
select 'test.tools' >= 'testsamba'; -- 0

-- mysql
select 'test.tools' >= 'testsamba'; -- 0

rustprintln!(">= {}", "test.tools" >= "testsamba"); // false

but

-- postgres
select 'test.tools' >= 'testsamba'; -- true


Reply to this email directly, view it on GitHub
https://github.com/lldap/lldap/issues/821#issuecomment-1918355835, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AAGCPWLUWMDBBY7VCTDKQVTYRHBPHAVCNFSM6AAAAABCP3HKQSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMJYGM2TKOBTGU
.
You are receiving this because you were mentioned.Message ID:
@.***>

<!-- gh-comment-id:1918445041 --> @nitnelave commented on GitHub (Jan 31, 2024): Ah, no, itertools is going to break since it'll advance the wrong one... On Wed, 31 Jan 2024, 07:04 Valentin Tolmer, ***@***.***> wrote: > Huh, I didn't think that string comparison would be a sticking point. And > if you remove the assert, are things broken? Both lists should be in the > same order, even if it's not the Rust order. > > On Wed, 31 Jan 2024, 05:24 Vladimir Dronnikov, ***@***.***> > wrote: > >> Well, >> >> -- sqlite3 >> select 'test.tools' >= 'testsamba'; -- 0 >> >> -- mysql >> select 'test.tools' >= 'testsamba'; -- 0 >> >> # rustprintln!(">= {}", "test.tools" >= "testsamba"); // false >> >> but >> >> -- postgres >> select 'test.tools' >= 'testsamba'; -- true >> >> — >> Reply to this email directly, view it on GitHub >> <https://github.com/lldap/lldap/issues/821#issuecomment-1918355835>, or >> unsubscribe >> <https://github.com/notifications/unsubscribe-auth/AAGCPWLUWMDBBY7VCTDKQVTYRHBPHAVCNFSM6AAAAABCP3HKQSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMJYGM2TKOBTGU> >> . >> You are receiving this because you were mentioned.Message ID: >> ***@***.***> >> >
Author
Owner

@dvv commented on GitHub (Jan 31, 2024):

I fix the issue altering the collation of the database.
This one may be closed.
Will you pull filters' query so that no long IN (...) live in the main branch?

<!-- gh-comment-id:1918451713 --> @dvv commented on GitHub (Jan 31, 2024): I fix the issue altering the collation of the database. This one may be closed. Will you pull filters' query so that no long ` IN (...)` live in the main branch?
Author
Owner

@nitnelave commented on GitHub (Feb 2, 2024):

@dvv sure. Can you give me the SQL command you ran to update the collation? I'd like to include that in the migration scripts.

<!-- gh-comment-id:1923963647 --> @nitnelave commented on GitHub (Feb 2, 2024): @dvv sure. Can you give me the SQL command you ran to update the collation? I'd like to include that in the migration scripts.
Author
Owner

@nitnelave commented on GitHub (Feb 2, 2024):

On second thought, it shouldn't be necessary: the order should be the same, so just removing the assert should be enough.

<!-- gh-comment-id:1923977530 --> @nitnelave commented on GitHub (Feb 2, 2024): On second thought, it shouldn't be necessary: the order should be the same, so just removing the assert should be enough.
Author
Owner

@dvv commented on GitHub (Feb 2, 2024):

@nitnelave first I tried https://stackoverflow.com/a/61595309

but then just recreated database as follows:

  lldap-db:
    image: postgres:16.1
    environment:
      ...
      LC_COLLATE: C
<!-- gh-comment-id:1923995870 --> @dvv commented on GitHub (Feb 2, 2024): @nitnelave first I tried https://stackoverflow.com/a/61595309 but then just recreated database as follows: ```docker-compose lldap-db: image: postgres:16.1 environment: ... LC_COLLATE: C ```
Author
Owner

@dvv commented on GitHub (Feb 2, 2024):

Right, I believe we may rely on database sort order -- be it this or that -- the point is to just compact related records, imo.

<!-- gh-comment-id:1924001409 --> @dvv commented on GitHub (Feb 2, 2024): Right, I believe we _may rely_ on database sort order -- be it this or that -- the point is to just compact related records, imo.
Author
Owner

@nitnelave commented on GitHub (Feb 27, 2024):

On third thought, I think the itertools utility used for grouping uses the ordering, so we have to re-sort.

<!-- gh-comment-id:1965955266 --> @nitnelave commented on GitHub (Feb 27, 2024): On third thought, I think the itertools utility used for grouping uses the ordering, so we have to re-sort.
Author
Owner

@nitnelave commented on GitHub (Oct 30, 2024):

I think this doesn't apply anymore. Closing

<!-- gh-comment-id:2447264092 --> @nitnelave commented on GitHub (Oct 30, 2024): I think this doesn't apply anymore. Closing
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/lldap-lldap#294
No description provided.