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