Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
30 changes: 30 additions & 0 deletions Week3/Assignments/SQLInjection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
function getPopulation(Country, name = "' OR '1'='1", code = "' OR '1'='1", cb) {
// assuming that connection to the database is established and stored as conn
conn.query(
`SELECT Population FROM ${Country} WHERE Name = '${name}' and code = '${code}'`,
[Country, name, code],
function (err, result) {
if (err) cb(err);
if (result.length == 0) cb(new Error("Not found"));
cb(null, result[0].name);
}
);
}
// the query becomes SELECT Population FROM Country WHERE Name = '' OR '1'='1' AND code = '' OR '1'='1';
// which is always true and returns the population of all countries
// to fix this we can use parameterized queries and whitelist for table names
function getPopulation(Country, name, code, cb) {
const allowedTables=['Country'];
if(!allowedTables.includes(Country)){
throw new Error('Invalid table');
}
conn.query(
`SELECT Population FROM ${Country} WHERE Name = $1 and code = $2`,
[name, code],
function (err, result) {
if (err) cb(err);
if (result.length == 0) cb(new Error("Not found"));
cb(null, result[0].Population);
}
);
}
24 changes: 24 additions & 0 deletions Week3/Assignments/account_changes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const {client}=require('./connection.js');
async function makeTransaction(){
try{
await client.query('BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE');
const res=await client.query('select balance from account where account_number =$1',[101]);
console.log('Current balance for account 101:', res.rows[0].balance);
if(res.rows[0].balance<=1000){
throw new Error('Insufficient funds in account 101');
}
Comment on lines +7 to +9

Choose a reason for hiding this comment

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

Nice check of balance beforehand. You can also check whether the accounts exist.

await client.query('UPDATE account SET balance=balance - $1 WHERE account_number=$2',[1000,101]);
await client.query('UPDATE account SET balance=balance + $1 WHERE account_number=$2',[1000,102]);
await client.query('INSERT INTO account_changes (account_number,amount,remark) VALUES ($1,$2,$3)',[101,-1000,'Transfer to account 102']);
await client.query('INSERT INTO account_changes(account_number,amount,remark) VALUES ($1,$2,$3)',[102,1000,'Transfer from account 101']);
await client.query('COMMIT');
console.log('Transaction completed successfully');
}
catch(err){
await client.query('ROLLBACK');
console.error('Error making transaction', err.message);
}
finally{
await client.end();
}
}
15 changes: 15 additions & 0 deletions Week3/Assignments/connection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { Client } = require('pg');

const client = new Client({
user: 'hyfuser',
host: 'localhost',
database: 'FinanceDB',
password: 'hyfpassword',
port: 5432,
});

client.connect()
.then(() => console.log('Connected to PostgreSQL'))
.catch(err => console.error('Connection error', err.stack));

module.exports = client;
55 changes: 55 additions & 0 deletions Week3/Assignments/exercise1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Database Normalization Answers

## 1. Columns that violate 1NF

The following columns violate 1NF because they contain multiple values in a single cell:
- `food_code` (e.g., `C1, C2`)
- `food_description` (e.g., `Curry, Cake`)

## 2. Recognized Entities

Entities that can be extracted:

1. **Member**
- Columns: `member_id`, `member_name`, `member_address`
2. **Dinner**
- Columns: `dinner_id`, `dinner_date`, `venue_code`
3. **Venue**
- Columns: `venue_code`, `venue_description`
4. **Food**
- Columns: `food_code`, `food_description`

**Relationships:**
- Many-to-many between Member and Dinner → junction table `Member_Dinner`
- Many-to-many between Dinner and Food → junction table `Dinner_Food`
- One-to-Many between dinner and venue

## 3. 3NF Compliant Tables and Columns

### Member
- `member_id` (PK)
- `member_name`
- `member_address`

### Venue
- `venue_code` (PK)
- `venue_description`

### Dinner
- `dinner_id` (PK)
- `dinner_date`
- `venue_code` (FK → Venue)

### Food
- `food_code` (PK)
- `food_description`

### Member_Dinner (junction table)
- `member_id` (FK → Member)
- `dinner_id` (FK → Dinner)
- PK = (`member_id`, `dinner_id`)

### Dinner_Food (junction table)
- `dinner_id` (FK → Dinner)
- `food_code` (FK → Food)
- PK = (`dinner_id`, `food_code`)
50 changes: 50 additions & 0 deletions Week3/Assignments/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pkg from "pg";
const { Client } = pkg;
const client1 = new Client({
user: "postgres", // change if needed
host: "localhost",
database: "testdb", // change if needed
password: "yourpassword", // change if needed
port: 5432,
});
const client2 = new Client({
user: "postgres",
host: "localhost",
database: "testdb",
password: "yourpassword",
port: 5432,
});
async function run() {
await client1.connect();
await client2.connect();
try {
// Transaction A (withdraw 100)
const txA = async () => {
await client1.query("BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE");
const res = await client1.query("SELECT balance FROM accounts WHERE account_id = 1");
console.log("TxA sees balance:", res.rows[0].balance);
await new Promise(r => setTimeout(r, 2000)); // simulate delay
await client1.query("UPDATE accounts SET balance = balance - 100 WHERE account_id = 1");
await client1.query("COMMIT");
console.log("TxA committed");
};
// Transaction B (withdraw 100 at the same time)
const txB = async () => {
await client2.query("BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE");
const res = await client2.query("SELECT balance FROM accounts WHERE account_id = 1");
console.log("TxB sees balance:", res.rows[0].balance);
await new Promise(r => setTimeout(r, 1000)); // simulate overlap
await client2.query("UPDATE accounts SET balance = balance - 100 WHERE account_id = 1");
await client2.query("COMMIT");
console.log("TxB committed");
};
// Run both "at the same time"
await Promise.allSettled([txA(), txB()]);
} catch (err) {
console.error("Error:", err.message);
} finally {
await client1.end();
await client2.end();
}
}
run();
30 changes: 30 additions & 0 deletions Week3/Assignments/transaction-creat-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const{client}=require('./connection.js');

async function createTables(){
try{
await client.query('DROP TABLE IF EXISTS account_changes');
await client.query('DROP TABLE IF EXISTS account');

const createAccountTableQuery=`CREATE TABLE account(
account_number INT PRIMARY KEY,
balance NUMERIC(12,2) NOT NULL);`;
const creatAccount_changesTableQuery=`CREATE TABLE account_changes(
change_number SERIAL PRIMARY KEY,
account_number INT ,
AMOUNT NUMERIC(12,2) NOT NULL,
changed_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
remark VARCHAR(255),
FOREIGN KEY (account_number) REFERENCES account(account_number) ON DELETE CASCADE);`;
await client.query(createAccountTableQuery);
await client.query(creatAccount_changesTableQuery);
console.log('Tables created successfully');


}
catch(err){
console.error('Error creating tables', err.message);
}finally{
await client.end();
}
}
createTables();
33 changes: 33 additions & 0 deletions Week3/Assignments/transaction-insert-values.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const {client}=require('./connection.js');
async function insertValues(){
try{
const insertAccountQuery=`INSERT INTO account (account_number,balance) VALUES ($1,$2),($3,$4),($5,$6),($7,$8)
ON CONFLICT (account_number) DO NOTHING;`;
const values=[
[101, 1000],
[102, 2000],
[103, 1500],
[104, 3000]
];
const res=await client.query(insertAccountQuery, values);
console.log('Inserted account numbers:', res.rows);
const insertAccountChangesQuery=`INSERT INTO account_changes (account_number, amount, remark) VALUES
($1,$2,$3),($4,$5,$6),($7,$8,$9),($10,$11,$12)
`;
const changesValues=[
[101, 2000, 'Initial deposit'],
[102, 500, 'Initial deposit'],
[103, 1000, 'Initial deposit'],
[104, 300, 'Initial deposit']
];

await client.query(insertAccountChangesQuery, changesValues);
console.log('Inserted values into account_changes table');
}
catch(err){
console.error('Error inserting values', err.message);
}finally{
await client.end();
}
}
insertValues();
43 changes: 35 additions & 8 deletions Week3/homework/mongodb/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
require('dotenv').config();
const { MongoClient, ServerApiVersion } = require("mongodb");


const { seedDatabase } = require("./seedDatabase.js");

async function createEpisodeExercise(client) {
const res=await client.db('DatabaseWeek3').collection('bob_ross_episodes').insertOne({
episode: 'S09E13',
title: 'MOUNTAIN HIDE-AWAY',
elements: ["CIRRUS", "CLOUDS", "CONIFER", "DECIDIOUS", "GRASS", "MOUNTAIN", "MOUNTAINS", "RIVER", "SNOWY_MOUNTAIN", "TREE", "TREES"]
})
/**
* We forgot to add the last episode of season 9. It has this information:
*
Expand All @@ -14,11 +21,12 @@ async function createEpisodeExercise(client) {
// Write code that will add this to the collection!

console.log(
`Created season 9 episode 13 and the document got the id ${"TODO: fill in variable here"}`
`Created season 9 episode 13 and the document got the id ${res.insertedId}`
);
}

async function findEpisodesExercises(client) {
const res=await client.db('DatabaseWeek3').collection('bob_ross_episodes').findOne({episode:'S02E02'})
/**
* Complete the following exercises.
* The comments indicate what to do and what the result should be!
Expand All @@ -27,29 +35,39 @@ async function findEpisodesExercises(client) {
// Find the title of episode 2 in season 2 [Should be: WINTER SUN]

console.log(
`The title of episode 2 in season 2 is ${"TODO: fill in variable here"}`
`The title of episode 2 in season 2 is ${res.title}`
);
const res2=await client.db('DatabaseWeek3').collection('bob_ross_episodes').findOne({title:'BLACK RIVER'})

// Find the season and episode number of the episode called "BLACK RIVER" [Should be: S02E06]

console.log(
`The season and episode number of the "BLACK RIVER" episode is ${"TODO: fill in variable here"}`
`The season and episode number of the "BLACK RIVER" episode is ${res2.episode}`
);


// Find all of the episode titles where Bob Ross painted a CLIFF [Should be: NIGHT LIGHT, EVENING SEASCAPE, SURF'S UP, CLIFFSIDE, BY THE SEA, DEEP WILDERNESS HOME, CRIMSON TIDE, GRACEFUL WATERFALL]
const cliffEpisodes=await client.db('DatabaseWeek3').collection('bob_ross_episodes').find({elements:{$in:['CLIFF']}}).toArray();
const titles=cliffEpisodes.map(episode=>episode.title);

console.log(
`The episodes that Bob Ross painted a CLIFF are ${"TODO: fill in variable here"}`
`The episodes that Bob Ross painted a CLIFF are ${titles.join(", ")}`
);


// Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT]
const cliffAndLighthouseEpisodes=await client.db('DatabaseWeek3').collection('bob_ross_episodes').find({elements:{$all:['CLIFF','LIGHTHOUSE']}}).toArray();
const cliffAndLighthouseTitles=cliffAndLighthouseEpisodes.map(episode=>episode.title);

console.log(
`The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${"TODO: fill in variable here"}`
`The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${cliffAndLighthouseTitles.join(", ")}`
);
}

async function updateEpisodeExercises(client) {
const updateResult=await client.db('DatabaseWeek3').collection('bob_ross_episodes').updateOne({
episode:'S30E13'
},{$set:{title:'BLUE RIDGE FALLS'}})
/**
* There are some problems in the initial data that was filled in.
* Let's use update functions to update this information.
Expand All @@ -60,15 +78,23 @@ async function updateEpisodeExercises(client) {
// Episode 13 in season 30 should be called BLUE RIDGE FALLS, yet it is called BLUE RIDGE FALLERS now. Fix that

console.log(
`Ran a command to update episode 13 in season 30 and it updated ${"TODO: fill in variable here"} episodes`
`Ran a command to update episode 13 in season 30 and it updated ${updateResult.modifiedCount} episodes`
);
const updateManyResult = await client
.db('DatabaseWeek3')
.collection('bob_ross_episodes')
.updateMany(
{ elements: { $in: ['BUSHES'] } },
{ $set: { "elements.$[elem]": "BUSH" } },
{ arrayFilters: [{ "elem": "BUSHES" }] }
);

// Unfortunately we made a mistake in the arrays and the element type called 'BUSHES' should actually be 'BUSH' as sometimes only one bush was painted.
// Update all of the documents in the collection that have `BUSHES` in the elements array to now have `BUSH`
// It should update 120 episodes!

console.log(
`Ran a command to update all the BUSHES to BUSH and it updated ${"TODO: fill in variable here"} episodes`
`Ran a command to update all the BUSHES to BUSH and it updated ${updateManyResult.modifiedCount} episodes`
);
}

Expand All @@ -77,9 +103,10 @@ async function deleteEpisodeExercise(client) {
* It seems an errand episode has gotten into our data.
* This is episode 14 in season 31. Please remove it and verify that it has been removed!
*/
const deleteResult=await client.db('DatabaseWeek3').collection('bob_ross_episodes').deleteOne({episode:'S31E14'})

Choose a reason for hiding this comment

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

Suggestion: you may notice client.db('DatabaseWeek3').collection('bob_ross_episodes') appears multiple times in this file. In this case, you can extract it into another function. You can also have two string constants for "databaseWeek3" and "bob_ross_episodes", so you can reuse those strings and avoid typos in future. And in future if you want to change database name, you don't need to change them one by one.


console.log(
`Ran a command to delete episode and it deleted ${"TODO: fill in variable here"} episodes`
`Ran a command to delete episode and it deleted ${deleteResult.deletedCount} episodes`
);
}

Expand Down
Loading