Either I'm confused or the gc is

donallen donaldcallen at gmail.com
Fri Oct 23 12:47:11 UTC 2020


On Thursday, 22 October 2020 at 21:58:04 UTC, rikki cattermole 
wrote:
> On 23/10/2020 4:56 AM, donallen wrote:
>> ==3854==    by 0x1B7E98: void 
>> verifier.main(immutable(char)[][]).walk_account_tree(verifier.main(immutable(char)[][]).Account, int) (verifier.d:272)
>> ==3854==  Uninitialised value was created by a stack allocation
>> ==3854==    at 0x1B77FC: void 
>> verifier.main(immutable(char)[][]).walk_account_tree(verifier.main(immutable(char)[][]).Account, int) (verifier.d:84)
>> ==3854==
>> ==3854== Conditional jump or move depends on uninitialised 
>> value(s)
>
> That is coming up an awful lot.
>
> We'd need walk_account_tree source, to know if its doing 
> something funky.
>
> But upon reviewing how the entire thing is working, I think we 
> need one_row and next_row_available_p source as well. Oh and 
> reset_stmt too.

Here's the source code you requested:

     void walk_account_tree(Account account, int ancestor_flags)
     {
         void fix_missing_commodity()
         {
             immutable string new_commodity_guid = 
one_row(new_guid, &get_string).string_value;
             // Create new commodity
             bind_text(insert_new_commodity, 1, 
new_commodity_guid);
             bind_text(insert_new_commodity, 2, account.name);
             run_dm_stmt(insert_new_commodity, &reset_stmt);

             // And link the account to the new commodity
             bind_text(link_to_commodity, 1, new_commodity_guid);
             bind_text(link_to_commodity, 2, account.name);
             run_dm_stmt(link_to_commodity, &reset_stmt);
         }

         void check_and_repair_commodity_link()
         {
             if (account.commodity_guid.length == 0)
             {
                 bind_text(get_possible_commodity_guid, 1, 
account.name);
                 immutable Maybe maybe_commodity_guid = 
maybe_one_row(get_possible_commodity_guid,
                         &get_string);
                 if (!maybe_commodity_guid.valid_p)
                 {
                     // No commodity exists with the same name as 
the account.  Create one and link the account to it.
                     writeln(account.path,
                             " requires a link to a commodity but 
doesn't have one. A commodity
with the same name as the account does not exist. One will be 
created
with the symbol **unknown** and the account will be linked to it. 
If
you wish to get quotes for this commodity, you will need to fix 
the
symbol in Newcash.");
                     fix_missing_commodity();
                 }
                 else
                 {
                     writeln(account.path, " requires a link to a 
commodity but doesn't have one.
A commodity with the same name as the account does exist. The 
account will be linked to it.");
                     bind_text(link_to_commodity, 1, 
maybe_commodity_guid.value.string_value);
                     bind_text(link_to_commodity, 2, account.guid);
                     run_dm_stmt(link_to_commodity, &reset_stmt);
                 }
             }
             else
             {
                 sqlite3_reset(get_possible_commodity_guid);
                 /* This marketable account has a commodity. Check 
to be sure that the guid is valid.
            If not, set the account's commodity_guid to NULL and 
process this account again. */
                 bind_text(verify_commodity_guid, 1, 
account.commodity_guid);
                 Maybe maybe_valid_guid = 
maybe_one_row(verify_commodity_guid, &get_string);
                 if (!maybe_valid_guid.valid_p)
                 {
                     writeln(account.path,
                             " requires a commodity link and has 
one, but the commodity guid is
invalid. Creating a new commodity with the same name as the 
account and
linking the account to it.");
                     fix_missing_commodity();
                 }
                 else
                 {
                     // Warn if the commodity name is not the same 
as the account name
                     bind_text(check_commodity_name, 1, 
account.guid);
                     MultiType n = one_row(check_commodity_name, 
&get_int);
                     if (n.integer_value == 0)
                     {
                         writeln(account.path, " requires a 
commodity link and has one, but the
commodity name is not the same as the account name. Is this 
intentional?");
                     }
                 }
             }
         }

         // Placeholder?
         if ((account.flags & account_flag_placeholder) == 0)
         {
             // No. Is this account an asset?
             if ((ancestor_flags & 
account_flag_descendents_are_assets) != 0)
             {
                 // Yes. Is it marketable?
                 if ((ancestor_flags & 
account_flag_descendents_are_marketable) != 0)
                 {
                     // Yes, account is a marketable asset. Check 
that it is linked to a proper commodity.
                     check_and_repair_commodity_link();
                 }
                 else
                 {
                     // No
                     if (account.commodity_guid.length > 0)
                     {
                         // This account is a non-marketable asset 
and has a non-null commodity guid.
                         // Set to NULL. Non-marketable accounts 
should not point to commodities.
                         writeln(account.path,
                                 " %s is a non-marketable asset, 
but it is associated with a
commodity. Removing the association by setting the account's 
commodity link to NULL.");
                         bind_text(nullify_commodity_guid, 1, 
account.guid);
                         run_dm_stmt(nullify_commodity_guid, 
&reset_stmt);
                     }
                     // Make sure the quantity is zero. Should not 
be otherwise for a non-marketable asset.
                     bind_text(check_quantities, 1, account.guid);
                     if (one_row(check_quantities, 
&get_int).integer_value > 0)
                     {
                         writeln(account.path,
                                 " is a non-marketable asset 
account but has splits with non-zero quantities.
These will be fixed.");
                         bind_text(fix_quantities, 1, 
account.guid);
                         run_dm_stmt(fix_quantities, &reset_stmt);
                     }
                 }
             }
             else
             {
                 // This account is not an asset. Make sure it 
doesn't point to a commodity,
                 // unless it is an Income account and inherits 
the descendents-need-commodity property.
                 // Does it need a commodity link?
                 if ((ancestor_flags & 
account_flag_descendents_need_commodity_link) != 0)
                 {
                     // Yes. Is it an income account?
                     if ((ancestor_flags & 
account_flag_descendents_are_income) != 0)
                     {
                         // Yes
                         check_and_repair_commodity_link();
                     }
                     else
                     {
                         writeln("The account ", account.path,
                                 " is not an Asset or Income 
account, but inherits the
'needs commodity' property. This should not be possible and
indicates a bug in Newcash or in the Verifier. Please report to 
Don Allen.");
                     }
                 }
                 else
                 {
                     if (account.commodity_guid.length != 0)
                     {
                         // This account is not an asset, doesn't 
have the needs-commodity-link property and has a non-null 
commodity guid, which we set to NULL.
                         writeln(account.path, " is not an asset, 
doesn't have the needs-commodity-link property, but it is 
associated with a commodity.
Removing the association by setting the account's commodity link 
to NULL.");
                         bind_text(nullify_commodity_guid, 1, 
account.guid);
                         run_dm_stmt(nullify_commodity_guid, 
&reset_stmt);
                     }
                     // The account is not an asset and does not 
have needs-commodity-link property.
                     // Just make sure it doesn't inherit the 
marketable property
                     if ((ancestor_flags & 
account_flag_descendents_are_marketable) != 0)
                     {
                         writeln(account.path,
                                 " is designated marketable, but 
is not an asset account. This
should not be possible and is indicative of a Newcash bug. Please 
report this to Don Allen.");
                     }
                 }
                 // Make sure the quantity is zero. Should not be 
otherwise for a non-asset.
                 bind_text(check_quantities, 1, account.guid);
                 int temp;
                 if ((temp = one_row(check_quantities, 
&get_int).integer_value) > 0)
                 {
                     writeln(account.path,
                             " %s is not an asset account but has 
splits with non-zero quantities. These will be fixed.");

                     bind_text(fix_quantities, 1, account.guid);
                     run_dm_stmt(fix_quantities, &reset_stmt);
                 }
             }
         }
         else
         {
             // This account is a place-holder. Verify that it has 
no transactions. Warn the user if that is not true.
             bind_text(count_transactions, 1, account.guid);
             int transaction_count = one_row(count_transactions, 
&get_int).integer_value;
             if (transaction_count > 0)
             {
                 writeln(account.path,
                         " is a placeholder account, but it has %d 
transactions. You should
re-assign them to an appropriate account with Newcash.");
             }
         }
         // Now do the children of this account
         // First determine how many there are
         bind_text(count_children, 1, account.guid);
         int n_children = one_row(count_children, 
&get_int).integer_value;
         if (n_children > 0)
         {
             Account[] children = new Account[n_children];
             bind_text(find_children, 1, account.guid);
             int i = 0;
             while (next_row_available_p(find_children, 
&reset_stmt))
             {
                 if (i > n_children-1)
                     panic("walk_account_tree: child index exceeds 
n_children");
                 children[i].name = 
fromStringz(sqlite3_column_text(find_children, 0)).dup;
                 children[i].path = format("%s:%s", account.path, 
children[i].name);
                 children[i].guid = 
fromStringz(sqlite3_column_text(find_children, 1)).dup; // guid
                 children[i].commodity_guid = 
fromStringz(sqlite3_column_text(find_children, 2)).dup; // 
commodity_guid
                 children[i].flags = 
sqlite3_column_int(find_children,
                         3) | account.flags & 
(account_flag_descendents_are_assets
                         | 
account_flag_descendents_are_liabilities | 
account_flag_descendents_are_income
                         | account_flag_descendents_are_expenses | 
account_flag_descendents_are_marketable
                         | 
account_flag_self_and_descendents_are_tax_related
                         | 
account_flag_descendents_need_commodity_link); // flags
                 i = i + 1;
             }
             foreach (child; children) {
                 walk_account_tree(child, ancestor_flags | 
account.flags);
             }
         }
     }

alias one_row_value_delegate = bool delegate (sqlite3_stmt* stmt, 
MultiType* result);
alias one_row_value_function = bool function (sqlite3_stmt* stmt, 
MultiType* result);
MultiType one_row(T)(sqlite3_stmt* stmt, T get_values)
{
     MultiType result;
     if (next_row_available_p(stmt, &reset_stmt))
     {
         if (get_values(stmt, &result))
         { // Did we get a value of the type requested?
             // Yes. Now make sure we only got one row back. This 
will also reset the stmt.
             if (next_row_available_p(stmt, &reset_stmt))
                 panic("one_row: stmt returned more than one row");
             return result;
         }
         else
             panic("one_row: stmt returned a null value");
     }
     else
         panic("one_row: stmt returned no rows");
     return result; // Need this for compiler happiness -- can't 
get here
}

/// Check for availability of another row
bool next_row_available_p(sqlite3_stmt* stmt, int 
function(sqlite3_stmt* stmt) cleanup)
{
     switch (sqlite3_step(stmt))
     {
     case sqlite_row:
         return (true);
     case sqlite_done:
         /* Make the prepared stmt available for re-use. */
         cleanup(stmt);
         return (false);
     default:
         stderr.writeln("next_row_available_p failed",);
         stderr.writeln(sqlite3_errmsg(sqlite3_db_handle(stmt)), 
stderr);
         exit(EXIT_FAILURE);
     }
     return (false); // Need this for compiler happiness -- can't 
get here
}

/// Reset so statement can be re-used
int reset_stmt(sqlite3_stmt* stmt)
{
     return sqlite3_reset(stmt);
}




More information about the Digitalmars-d mailing list