vineri, 12 decembrie 2025

When Learning Jumps Levels: Rethinking the Path from Simple to Advanced

I often say that with complex topics, we need to approach them step by step, so that the reader understands how we arrived there: from simple to complex, just as one of the core principles of education suggests, where sequencing matters.
And indeed, the order does matter. But it seems to me that sometimes certain concepts, even though they are more advanced than earlier ones and should logically come later, might actually represent the start of a different stage of thinking and therefore could deserve to be introduced first.

I often think about this: when a problem appears, the first solution that comes to mind is usually the trivial one. But sometimes it would be better if the advanced, optimal solution came to mind first.

***

 From https://youtu.be/X1HSGEADAhE?t=1573 : 

***

We even teach student programmers in their first years, when they still don’t understand what programming is and are seeing a computer for the first time. Many of them we teach to program in C or C++.

And then, once they’ve learned C or C++, a year or two passes, and in their third or fourth year, or even later when they’ve already become programmers, we start retraining or further educating them in proper design.

We tell them:
"You know, objects are actually certain entities, certain abstractions inside your program. And these objects should have the property of encapsulation. They should hide information."

The programmer—well, the C-style programmer—looks at this and it’s all new to them.
"What do you mean, hide information? How is it that they should encapsulate data and not expose it?"

Well, in C, everything is exposed. In C, there’s global state basically everywhere.
Sure, it’s not encouraged, but people still do it. Global variables are a common practice.

And suddenly, in their third or fourth year—or even in their fifteenth year of working as a programmer—they’re told that everything should have been done differently in OOP. And that’s the problem.

So Richard proposes:
"Let’s introduce them to OOP in the first year—before we even teach them how to program, before they write their first program. Let’s explain what OOP is, why objects are needed, what encapsulation and polymorphism are."

He believes they can understand it—even if it’s with sticks and pictures, with some auxiliary or educational languages.

***
 

joi, 13 noiembrie 2025

Avoiding the Planning Trap: Why N×M is a Planning Disaster (The Cartesian Product of Tasks)

I’ve been thinking a lot about planning lately. I realized I’ve fallen into the same trap many times, and understanding the root cause has made all the difference.

My first impulse was always to over-organize. I’d create a neat system:

  •  I'd create a separate folder and file for each family of tasks (e.g., "Work," "Personal," "Learning"). It felt great at the beginning—so structured! But after a few days, it became a hassle.
  •  The same feeling occurred when I tried building a complex goalscape, with first-level goals for task families, and then another level of goals for each individual task.

In both cases, I was creating a system that demanded too much energy just to maintain. 

*** 

I was unknowingly introducing an unnecessary dimension to my planning.

When I used multiple files, it felt like I was generating a final complexity of N x M, where N is the number of files (or task families) and M is the total number of tasks. 

*** 

Then, I tried the opposite: one single file.

In this file, I created a section for each task family, and a simple Table of Contents (ToC) at the beginning. Each task lives in its relevant section.

Immediately, the system simplified. By removing the file-switching dimension, the complexity was converted to a simple line: M, the total number of tasks. 

***

This is exactly like when I analyze code.

If I am analyzing a method x() with N lines, and it calls another method y() with M lines, it’s easy to get lost by constantly jumping: analyze a line in x(), then go deep into y(), then jump back to x(), and so on.
 

The better approach is to stay at the level of x(), or finish analyzing y() completely, but you must avoid the inefficient back-and-forth.

 

duminică, 19 octombrie 2025

PostgreSQL: ERROR: missing chunk number 0 for toast value 123456 in pg_toast_2619

Problem: After database migration

SELECT * FROM <table_name>

fails with the following message:

ERROR: missing chunk number 0 for toast value 123456 in pg_toast_2619


At first, I found https://gist.github.com/supix/80f9a6111dc954cf38ee99b9dedf187a and https://newbiedba.wordpress.com/2015/07/07/postgresql-missing-chunk-0-for-toast-value-in-pg_toast/, but I could not isolate corrupted row, i.e. every query of the following form
SELECT * FROM <table_name> ORDER BY id LIMIT 5000 OFFSET 0;
SELECT * FROM <table_name> ORDER BY id LIMIT 5000 offset 5000;
... 
even
SELECT * FROM <table_name> ORDER BY id LIMIT 1;
failed with the same error message.

A second article (see https://fluca1978.github.io/2021/02/08/PostgreSQLToastCorruption) together with a set of Postgres functions for detecting corrupted toasts (see https://gitlab.com/fluca1978/fluca1978-pg-utils/-/blob/master/examples/toast/find_bad_toast.sql) inspired me how to find the table backed by `pg_toast_2619`, since it was already clear that the problem was not in my <table_name>:
SELECT reltoastrelid, reltoastrelid::regclass, pg_relation_filepath( reltoastrelid::regclass ), relname
FROM   pg_class
WHERE  reltoastrelid = 'pg_toast.pg_toast_2619'::regclass
  AND  relkind = 'r';
The result was pg_statistic and I executed the inverse query to recheck:
SELECT reltoastrelid, reltoastrelid::regclass, pg_relation_filepath( reltoastrelid::regclass ), relname
FROM   pg_class
WHERE  relname = 'pg_statistic'
  AND  relkind = 'r';
the result was pg_toast_2619. Hence the problem was not in <table_name> but in pg_statistic, more precisely in the TOAST of pg_statistic. But why the SELECT * FROM <tabel_name> generated this problem? Because Postgres uses pg_statistic to plan the query. Meanwhile a collegue of mine 
found https://www.ongres.com/blog/solving-postgresql-statistic-corruption/. This article shows how to deal with such cases but also shows that pg_toast_2619 is somehow constant between different Postgres installations.

What I learned from this problem:
  1. How to find a table by its corresponding TOAST.
  2. There are hidden actions behind a simple SELECT * FROM <tabel_name>, i.e. PostgreSQL  can perform a reading from another table first and then from the table `<tabel_name>` (not everything is as it seems at first sight).

PostgreSQL: ERROR: Key (id)=(4703) is duplicated. could not create unique index ""

Problem: After database migration
REINDEX <tabel_name>;
fails with the following message:

ERROR:  Key (id)=(4703) is duplicated. could not create unique index "<table_name>"

It's very strange because a simple
SELECT * FROM <table_name> WHERE id = 4703;
returns only one row. So where does REINDEX find the second (or more) row with id = 4703?

After searching in Google, with "Key (id)=(4703) is duplicated. could not create unique index", I found  
https://stackoverflow.com/questions/45315510/postgres-could-not-create-unique-index-key-is-duplicated, 
that inspired me to try 
SET LOCAL enable_indexscan = off;
SET LOCAL enable_bitmapscan = off;
SET LOCAL enable_indexonlyscan = off;
SELECT * FROM <table_name> WHERE id = 4703;
This query returned two rows. To understand the difference in behavior modify both queries to execute an EXPLAIN SELECT * FROM <table_name> ... instead of SELECT * FROM <table_name> ... and you'll see that Postgres is using the index in the first query to search for the row, that is why 
we don't see the second row, it's not present in the index (by the definition of the index). While the second query is searching for it physically in the table and thus finds both rows.

Why there are two rows with the same id, is another question. My assumption is that one row was marked as deleted in the source database and somehow the data was corrupted and it appeared as non deleted in the destination database. The database was migrated byte by byte, not using .sql export/import.

What I learned from this problem:

  1. How to force Postgres not to use (or use) table's index for a specific query.
  2. A SELECT * FROM <table_name> in most cases is not a physical read of `<table_name>`, i.e. more generally, in Postgres, not everything is as it seems. 

miercuri, 20 august 2025

Debugging Hotel Wi-Fi Login on Arch Linux (and a Docker Gotcha)

Recently, while staying at a hotel, I tried connecting my Arch Linux laptop to the Wi-Fi. As usual, the network redirected to a captive portal login page. But this time, things didn’t work as expected.

First, I checked the captive portal detection URL with:

curl -I http://detectportal.firefox.com

That gave me a gateway URL like:

http://127.17.0.1/login.php

But when I tried to open it in a browser, I kept getting “Unable to connect”.

At first, I thought the hotel’s Wi-Fi was broken. Then I noticed something odd: the IP address started with 127.17…. That reminded me of Docker, since I had seen a similar subnet in docker info.

So I ran:

ip addr show

And sure enough, I saw this:

docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:84:b7:e7:1e brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:84ff:feb7:e71e/64 scope link proto kernel_ll

Aha! Docker was hijacking the IP range 172.17.x.x, which happened to be the same network the hotel’s captive portal was using.

To fix it, I temporarily disabled the Docker bridge:

sudo ip link set docker0 down
sudo ip addr flush dev docker0

After that, I could finally access:

http://172.17.0.1/login.php

and log in to the Wi-Fi.

marți, 12 august 2025

Quickly Format a List for SQL IN Clause Using Notepad++

Let’s say I have a list of strings:

STRING1
STRING2
...
STRING200

And I want to create a query like:

SELECT count(*) 
FROM TABLE 
WHERE COLUMN IN ('STRING1', 'STRING2', ..., 'STRING200');

Quick solution using Notepad++:

  1. Open the file in Notepad++.
  2. In the Find what field, enter:
    ^(.*)$
    
  3. In the Replace with field, enter:
    '$1',
  4. Set Search Mode to Regular expression.
  5. Click Replace All.
This will wrap each string in quotes and add a trailing comma, so you can paste it directly into your SQL IN clause.

joi, 24 iulie 2025

VS Code "Open Folder" Does Nothing on Arch Linux (LXQt)

I installed Visual Studio Code on Arch Linux with LXQt, but clicking "Open Folder" did nothing — no error, no folder picker, just silence.

To debug, I ran:

journalctl -f

Then clicked "Open Folder" again. This time, I saw the error:

xdg-desktop-portal: Backend call failed: Could not activate remote peer 'org.freedesktop.impl.portal.desktop.gtk': activation request failed: unit is masked

Fix:

systemctl --user unmask xdg-desktop-portal-gtk.service

systemctl --user enable --now xdg-desktop-portal-gtk.service