Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
88 changes: 88 additions & 0 deletions Week3/assignmentWeek3/ex1 - normalization.md

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all correct! The layout and language are triggering my AI-spider sense a bit. If you figured all of this out on your own, I'm completely ok with it if you asked ChappyG to format it. For me the most important thing is that you know what and why things are happening.

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Exercise 1: SQL Normalization – Dinner Club

## Step 1: Violation of 1NF (First Normal Form)

**1NF Rule**: Each field must contain only **atomic** (indivisible) values.

### ❌ Violations of 1NF:
- **`food_code`** and **`food_description`** contain **multiple values** separated by commas (e.g., `"C1, C2"`, `"Curry, Cake"`).
- **`dinner_date`** has **inconsistent formats** (e.g., `"2020-03-15"`, `"20-03-2020"`, `"Mar 25 '20"`), violating domain consistency.

---

## Step 2: Recognized Entities

Based on the table, we can identify the following entities:

1. **Member** – identified by `member_id`.
2. **Dinner** – identified by `dinner_id`.
3. **Venue** – identified by `venue_code`.
4. **Food** – identified by `food_code`.
5. **Dinner Participation** – mapping between members and dinners (many-to-many).
6. **Dinner Food** – mapping between dinners and foods served (many-to-many).

---

## Step 3: 3NF-Compliant Tables and Attributes

### 1. **Members**
| Column Name | Type |
|-----------------|-------------|
| member_id (PK) | INTEGER |
| member_name | TEXT |
| member_address | TEXT |

---

### 2. **Venues**
| Column Name | Type |
|---------------------|---------|
| venue_code (PK) | TEXT |
| venue_description | TEXT |

---

### 3. **Dinners**
| Column Name | Type |
|-----------------|-----------|
| dinner_id (PK) | TEXT |
| dinner_date | DATE |
| venue_code (FK) | TEXT |

---

### 4. **Foods**
| Column Name | Type |
|--------------------|---------|
| food_code (PK) | TEXT |
| food_description | TEXT |

---

### 5. **Dinner_Participation** (Many-to-Many: members attend multiple dinners)
| Column Name | Type |
|--------------------|---------|
| member_id (FK) | INTEGER |
| dinner_id (FK) | TEXT |

**Primary Key**: (member_id, dinner_id)

---

### 6. **Dinner_Food** (Many-to-Many: dinners serve multiple foods)
| Column Name | Type |
|--------------------|---------|
| dinner_id (FK) | TEXT |
| food_code (FK) | TEXT |

**Primary Key**: (dinner_id, food_code)

---

## Summary of Normalization

- The **original table** violates **1NF** due to multi-valued fields and inconsistent formats.
- We **decomposed** the table into **six 3NF-compliant tables** by:
- Ensuring atomicity of values,
- Removing transitive and partial dependencies,
- Isolating repeating groups into separate relations.
68 changes: 68 additions & 0 deletions Week3/assignmentWeek3/ex2 - SQL Transactions/transaction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import mysql from "mysql2/promise";

const connection = await mysql.createConnection({
host: "localhost",
user: "hyfuser",
password: "hyfpassword",
database: "w3_assignment",
});

await connection.beginTransaction();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One could argue that we could move this into the try so that we can make sure to catch any errors with the starting of the transaction. But this is fine for now I think.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try, but i think that to add tyr block here i need to add function for the hole transaction. I will try with try. :)


const fromAccount = 101;
const toAccount = 102;
const amount = 1000;
const date = "2025-06-02";

const transactionFrom = {
account_number: fromAccount,
amount: -amount,
changed_date: date,
remark: `Transfer to account #${toAccount}`,
};

const transactionTo = {
account_number: toAccount,
amount: amount,
changed_date: date,
remark: `Transfer from account #${fromAccount}`,
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like these objects! They are clear and well structured!


try {
const [availableBalanceResult] = await connection.query(
"SELECT balance FROM account WHERE account_number = ?",
[fromAccount]
);

const availableBalance = availableBalanceResult[0]?.balance;

if (availableBalance < amount) {
throw new Error("Insufficient balance to perform the transfer");
}

console.log(`Account #${fromAccount} available balance: ${availableBalance}`);

// Deduct from sender
await connection.query("INSERT INTO account_changes SET ?", transactionFrom);
await connection.query(
"UPDATE account SET balance = balance + ? WHERE account_number = ?",
[transactionFrom.amount, fromAccount]
);
console.log(`$${amount} deducted from account #${fromAccount}`);

// Add to receiver
await connection.query("INSERT INTO account_changes SET ?", transactionTo);
await connection.query(
"UPDATE account SET balance = balance + ? WHERE account_number = ?",
[transactionTo.amount, toAccount]
);
console.log(`$${amount} added to account #${toAccount}`);

await connection.commit();
console.log("Transaction completed successfully.");
} catch (error) {
await connection.rollback();
console.error("Transaction failed:", error.message);
} finally {
await connection.end();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import mysql from 'mysql2/promise';

async function createTables() {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'hyfuser',
password: 'hyfpassword',
database: 'w3_assignment',
});

try {
await connection.beginTransaction();

await connection.query(`
CREATE TABLE IF NOT EXISTS account (
account_number INT PRIMARY KEY,
balance DECIMAL(12, 2) NOT NULL

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A 100 billion is a nice big number for this, well done!

)
`);

await connection.query(`
CREATE TABLE IF NOT EXISTS account_changes (
change_number INT AUTO_INCREMENT PRIMARY KEY,
account_number INT,
amount DECIMAL(12, 2) NOT NULL,
changed_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
remark TEXT,
FOREIGN KEY (account_number) REFERENCES account(account_number)
)
`);

await connection.commit();
console.log('Tables created successfully');
} catch (err) {
await connection.rollback();
console.error('Error creating tables:', err);
} finally {
await connection.end();
}
}

createTables();
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import mysql from 'mysql2/promise';

async function insertValues() {
const connection = await mysql.createConnection({
host: 'localhost',
user: 'hyfuser',
password: 'hyfpassword',
database: 'w3_assignment',
});

try {
await connection.beginTransaction();

await connection.query(`
INSERT INTO account (account_number, balance) VALUES
(101, 5000.00),
(102, 3000.00)
`);

await connection.query(`
INSERT INTO account_changes (account_number, amount, remark) VALUES
(101, 5000.00, 'Initial deposit'),
(102, 3000.00, 'Initial deposit')
`);

await connection.commit();
console.log('Values inserted successfully');
} catch (err) {
await connection.rollback();
console.error('Error inserting values:', err);
} finally {
await connection.end();
}
}

insertValues();
22 changes: 22 additions & 0 deletions Week3/assignmentWeek3/ex3 - SQL injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
1. - Give an example of a value that can be passed as name and code that would take advantage of SQL-injection and ( fetch all the records in the database)
- answer:
- SELECT Population FROM Countries WHERE Name = '' OR '1'='1' and code = '' OR '1'='1'
- I think it would work sinse '1'='1' is always true the query will return all rows in the table, ignoring filtering by name and code.

2.
```
function getPopulation(Country, name, code, cb) {
const allowedTables = ['Countries', 'OtherValidTable'];
if (!allowedTables.includes(Country)) {
return cb(new Error("Invalid table name"));
}

const sql = `SELECT Population FROM ${Country} WHERE Name = ? AND code = ?`;

conn.query(sql, [name, code], function (err, result) {
if (err) return cb(err);
if (result.length === 0) return cb(new Error("Not found"));
cb(null, result[0].Population);
});
}
```
Loading