From 4408a91f275b8a896c6af430d5f3e4e9d8a071ba Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 12:14:28 +0000 Subject: [PATCH 01/15] Update GitHub Classroom Autograding Workflow --- .github/workflows/classroom.yml | 222 ++++++++++++++++++++++++++++++-- 1 file changed, 211 insertions(+), 11 deletions(-) diff --git a/.github/workflows/classroom.yml b/.github/workflows/classroom.yml index dca83b024..f5a7f41d6 100644 --- a/.github/workflows/classroom.yml +++ b/.github/workflows/classroom.yml @@ -1,19 +1,219 @@ -name: GitHub Classroom Workflow - -on: - - push - - workflow_dispatch - +name: Autograding Tests +'on': +- workflow_dispatch +- repository_dispatch permissions: checks: write actions: read contents: read - jobs: - build: - name: Autograding + run-autograding-tests: runs-on: ubuntu-latest if: github.actor != 'github-classroom[bot]' steps: - - uses: actions/checkout@v4 - - uses: education/autograding@v1 + - name: Checkout code + uses: actions/checkout@v4 + - name: Step-1 Test + id: step-1-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-1 Test + setup-command: npm install + command: npm run test:1 + timeout: 10 + max-score: 10 + - name: Step-2 Test + id: step-2-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-2 Test + setup-command: npm install + command: npm run test:2 + timeout: 10 + max-score: 10 + - name: Step-3 Test + id: step-3-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-3 Test + setup-command: npm install + command: npm run test:3 + timeout: 10 + max-score: 10 + - name: Step-4 Test + id: step-4-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-4 Test + setup-command: npm install + command: npm run test:4 + timeout: 10 + - name: Step-5 Test + id: step-5-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-5 Test + setup-command: npm install + command: npm run test:5 + timeout: 10 + max-score: 10 + - name: Step-6 Test + id: step-6-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-6 Test + setup-command: npm install + command: npm run test:6 + timeout: 10 + max-score: 10 + - name: Step-7 Test + id: step-7-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-7 Test + setup-command: npm install + command: npm run test:7 + timeout: 10 + max-score: 10 + - name: Step-8 Test + id: step-8-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-8 Test + setup-command: npm install + command: npm run test:8 + timeout: 10 + max-score: 10 + - name: Step-9 Test + id: step-9-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-9 Test + setup-command: npm install + command: npm run test:9 + timeout: 10 + max-score: 10 + - name: Step-10 Test + id: step-10-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-10 Test + setup-command: npm install + command: npm run test:10 + timeout: 10 + max-score: 10 + - name: Step-11 Test + id: step-11-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-11 Test + setup-command: npm install + command: npm run test:11 + timeout: 10 + max-score: 10 + - name: Step-12 Test + id: step-12-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-12 Test + setup-command: npm install + command: npm run test:12 + timeout: 10 + max-score: 10 + - name: Step-13 Test + id: step-13-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-13 Test + setup-command: npm install + command: npm run test:13 + timeout: 10 + max-score: 10 + - name: Step-14 Test + id: step-14-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-14 Test + setup-command: npm install + command: npm run test:14 + timeout: 10 + max-score: 10 + - name: Step-15 Test + id: step-15-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-15 Test + setup-command: npm install + command: npm run test:15 + timeout: 10 + max-score: 10 + - name: Step-16 Test + id: step-16-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-16 Test + setup-command: npm install + command: npm run test:16 + timeout: 10 + max-score: 10 + - name: Step-17 Test + id: step-17-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-17 Test + setup-command: npm install + command: npm run test:17 + timeout: 10 + max-score: 10 + - name: Step-18 Test + id: step-18-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-18 Test + setup-command: npm install + command: npm run test:18 + timeout: 10 + max-score: 10 + - name: Step-19 Test + id: step-19-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-19 Test + setup-command: npm install + command: npm run test:19 + timeout: 10 + max-score: 10 + - name: Step-20 Test + id: step-20-test + uses: classroom-resources/autograding-command-grader@v1 + with: + test-name: Step-20 Test + setup-command: npm install + command: npm run test:20 + timeout: 10 + max-score: 10 + - name: Autograding Reporter + uses: classroom-resources/autograding-grading-reporter@v1 + env: + STEP-1-TEST_RESULTS: "${{steps.step-1-test.outputs.result}}" + STEP-2-TEST_RESULTS: "${{steps.step-2-test.outputs.result}}" + STEP-3-TEST_RESULTS: "${{steps.step-3-test.outputs.result}}" + STEP-4-TEST_RESULTS: "${{steps.step-4-test.outputs.result}}" + STEP-5-TEST_RESULTS: "${{steps.step-5-test.outputs.result}}" + STEP-6-TEST_RESULTS: "${{steps.step-6-test.outputs.result}}" + STEP-7-TEST_RESULTS: "${{steps.step-7-test.outputs.result}}" + STEP-8-TEST_RESULTS: "${{steps.step-8-test.outputs.result}}" + STEP-9-TEST_RESULTS: "${{steps.step-9-test.outputs.result}}" + STEP-10-TEST_RESULTS: "${{steps.step-10-test.outputs.result}}" + STEP-11-TEST_RESULTS: "${{steps.step-11-test.outputs.result}}" + STEP-12-TEST_RESULTS: "${{steps.step-12-test.outputs.result}}" + STEP-13-TEST_RESULTS: "${{steps.step-13-test.outputs.result}}" + STEP-14-TEST_RESULTS: "${{steps.step-14-test.outputs.result}}" + STEP-15-TEST_RESULTS: "${{steps.step-15-test.outputs.result}}" + STEP-16-TEST_RESULTS: "${{steps.step-16-test.outputs.result}}" + STEP-17-TEST_RESULTS: "${{steps.step-17-test.outputs.result}}" + STEP-18-TEST_RESULTS: "${{steps.step-18-test.outputs.result}}" + STEP-19-TEST_RESULTS: "${{steps.step-19-test.outputs.result}}" + STEP-20-TEST_RESULTS: "${{steps.step-20-test.outputs.result}}" + with: + runners: step-1-test,step-2-test,step-3-test,step-4-test,step-5-test,step-6-test,step-7-test,step-8-test,step-9-test,step-10-test,step-11-test,step-12-test,step-13-test,step-14-test,step-15-test,step-16-test,step-17-test,step-18-test,step-19-test,step-20-test From 075a0936fd9ca15f7d488e1459c590d38f77bb5e Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 12:14:29 +0000 Subject: [PATCH 02/15] GitHub Classroom Feedback --- .github/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/.keep diff --git a/.github/.keep b/.github/.keep new file mode 100644 index 000000000..e69de29bb From 9572d0d78534e0f8c398fd813b48f7c38c741879 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 12:14:29 +0000 Subject: [PATCH 03/15] Setting up GitHub Classroom Feedback From 8019bf6868912d2acf9eb13519695a342427668c Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 12:14:31 +0000 Subject: [PATCH 04/15] add online IDE url --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eadfc715a..e45b2a477 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Open in Visual Studio Code](https://classroom.github.com/assets/open-in-vscode-718a45dd9cf7e7f842a935f5ebbe5719a5e09af4491e668f4dbf3b35d5cca122.svg)](https://classroom.github.com/online_ide?assignment_repo_id=14973533&assignment_repo_type=AssignmentRepo)

StylusDB SQL

A SQL database engine written in JavaScript From 3a18a03060ca5db42ffb151bf5eb433edcb379cb Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:03:02 +0530 Subject: [PATCH 05/15] 01.md tutorial done --- src/index.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/index.js diff --git a/src/index.js b/src/index.js new file mode 100644 index 000000000..e69de29bb From 3f62ae542a072a5d5f8ddb2763181c9ff40b8613 Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:16:42 +0530 Subject: [PATCH 06/15] completed tutorial 2. Test cases passed: 2 --- sample.csv | 4 ++++ src/csvReader.js | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 sample.csv diff --git a/sample.csv b/sample.csv new file mode 100644 index 000000000..9e7a9fa25 --- /dev/null +++ b/sample.csv @@ -0,0 +1,4 @@ +id,name,age +1,John,30 +2,Jane,25 +3,Bob,22 \ No newline at end of file diff --git a/src/csvReader.js b/src/csvReader.js index e69de29bb..c39210a98 100644 --- a/src/csvReader.js +++ b/src/csvReader.js @@ -0,0 +1,20 @@ +const fs = require('fs'); +const csv = require('csv-parser'); + +function readCSV(filePath) { + const results = []; + + return new Promise((resolve, reject) => { + fs.createReadStream(filePath) + .pipe(csv()) + .on('data', (data) => results.push(data)) + .on('end', () => { + resolve(results); + }) + .on('error', (error) => { + reject(error); + }); + }); +} + +module.exports = readCSV; \ No newline at end of file From 483cf2eb7d6d58d557ab72a9d1902ff023585042 Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:19:53 +0530 Subject: [PATCH 07/15] completed tutorial 3. Test cases passed: 3 --- src/queryParser.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/queryParser.js diff --git a/src/queryParser.js b/src/queryParser.js new file mode 100644 index 000000000..8cba13b8a --- /dev/null +++ b/src/queryParser.js @@ -0,0 +1,16 @@ +function parseQuery(query) { + const selectRegex = /SELECT (.+) FROM (.+)/i; + const match = query.match(selectRegex); + + if (match) { + const [, fields, table] = match; + return { + fields: fields.split(',').map(field => field.trim()), + table: table.trim() + }; + } else { + throw new Error('Invalid query format'); + } +} + +module.exports = parseQuery; \ No newline at end of file From 8c37ca00c550a4adbf1f4a2de264a23bc625627f Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:25:48 +0530 Subject: [PATCH 08/15] completed tutorial 4. Test cases passed: 4 --- src/index.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/index.js b/src/index.js index e69de29bb..4abc0e143 100644 --- a/src/index.js +++ b/src/index.js @@ -0,0 +1,18 @@ +const parseQuery = require('./queryParser'); +const readCSV = require('./csvReader'); + +async function executeSELECTQuery(query) { + const { fields, table } = parseQuery(query); + const data = await readCSV(`${table}.csv`); + + // Filter the fields based on the query + return data.map(row => { + const filteredRow = {}; + fields.forEach(field => { + filteredRow[field] = row[field]; + }); + return filteredRow; + }); +} + +module.exports = executeSELECTQuery; \ No newline at end of file From e0680427074227d004320736bda30a8ec99636a3 Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:27:52 +0530 Subject: [PATCH 09/15] completed tutorial 5. Test cases passed: 5 --- src/queryParser.js | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/queryParser.js b/src/queryParser.js index 8cba13b8a..c0775a3d0 100644 --- a/src/queryParser.js +++ b/src/queryParser.js @@ -1,16 +1,26 @@ -function parseQuery(query) { - const selectRegex = /SELECT (.+) FROM (.+)/i; - const match = query.match(selectRegex); +const parseQuery = require('./queryParser'); +const readCSV = require('./csvReader'); - if (match) { - const [, fields, table] = match; - return { - fields: fields.split(',').map(field => field.trim()), - table: table.trim() - }; - } else { - throw new Error('Invalid query format'); - } +async function executeSELECTQuery(query) { + const { fields, table, whereClause } = parseQuery(query); + const data = await readCSV(`${table}.csv`); + + // Filtering based on WHERE clause + const filteredData = whereClause + ? data.filter(row => { + const [field, value] = whereClause.split('=').map(s => s.trim()); + return row[field] === value; + }) + : data; + + // Selecting the specified fields + return filteredData.map(row => { + const selectedRow = {}; + fields.forEach(field => { + selectedRow[field] = row[field]; + }); + return selectedRow; + }); } -module.exports = parseQuery; \ No newline at end of file +module.exports = executeSELECTQuery; \ No newline at end of file From d9c17ce33f0f855fb7c4c6cece0fc2a506cce71e Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:29:38 +0530 Subject: [PATCH 10/15] completed tutorial 6. Test cases passed: 7 --- src/index.js | 22 +++++++++++++++------- src/queryParser.js | 42 +++++++++++++++++++++--------------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/index.js b/src/index.js index 4abc0e143..93b8affea 100644 --- a/src/index.js +++ b/src/index.js @@ -2,16 +2,24 @@ const parseQuery = require('./queryParser'); const readCSV = require('./csvReader'); async function executeSELECTQuery(query) { - const { fields, table } = parseQuery(query); + const { fields, table, whereClauses } = parseQuery(query); const data = await readCSV(`${table}.csv`); - - // Filter the fields based on the query - return data.map(row => { - const filteredRow = {}; + + // Apply WHERE clause filtering + const filteredData = whereClauses.length > 0 + ? data.filter(row => whereClauses.every(clause => { + // You can expand this to handle different operators + return row[clause.field] === clause.value; + })) + : data; + + // Select the specified fields + return filteredData.map(row => { + const selectedRow = {}; fields.forEach(field => { - filteredRow[field] = row[field]; + selectedRow[field] = row[field]; }); - return filteredRow; + return selectedRow; }); } diff --git a/src/queryParser.js b/src/queryParser.js index c0775a3d0..82745d43b 100644 --- a/src/queryParser.js +++ b/src/queryParser.js @@ -1,26 +1,26 @@ -const parseQuery = require('./queryParser'); -const readCSV = require('./csvReader'); +function parseQuery(query) { + const selectRegex = /SELECT (.+?) FROM (.+?)(?: WHERE (.*))?$/i; + const match = query.match(selectRegex); -async function executeSELECTQuery(query) { - const { fields, table, whereClause } = parseQuery(query); - const data = await readCSV(`${table}.csv`); - - // Filtering based on WHERE clause - const filteredData = whereClause - ? data.filter(row => { - const [field, value] = whereClause.split('=').map(s => s.trim()); - return row[field] === value; - }) - : data; + if (match) { + const [, fields, table, whereString] = match; + const whereClauses = whereString ? parseWhereClause(whereString) : []; + return { + fields: fields.split(',').map(field => field.trim()), + table: table.trim(), + whereClauses + }; + } else { + throw new Error('Invalid query format'); + } +} - // Selecting the specified fields - return filteredData.map(row => { - const selectedRow = {}; - fields.forEach(field => { - selectedRow[field] = row[field]; - }); - return selectedRow; +function parseWhereClause(whereString) { + const conditions = whereString.split(/ AND | OR /i); + return conditions.map(condition => { + const [field, operator, value] = condition.split(/\s+/); + return { field, operator, value }; }); } -module.exports = executeSELECTQuery; \ No newline at end of file +module.exports = parseQuery; \ No newline at end of file From bb1ff02ab8daa399bbf7fcda3895e3e3a0df1680 Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:33:03 +0530 Subject: [PATCH 11/15] completed tutorial 7. Test cases passed: 9 --- src/index.js | 18 ++++++++++++++---- src/queryParser.js | 12 ++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/index.js b/src/index.js index 93b8affea..aba653378 100644 --- a/src/index.js +++ b/src/index.js @@ -7,10 +7,7 @@ async function executeSELECTQuery(query) { // Apply WHERE clause filtering const filteredData = whereClauses.length > 0 - ? data.filter(row => whereClauses.every(clause => { - // You can expand this to handle different operators - return row[clause.field] === clause.value; - })) + ? data.filter(row => whereClauses.every(clause => evaluateCondition(row, clause))) : data; // Select the specified fields @@ -23,4 +20,17 @@ async function executeSELECTQuery(query) { }); } +function evaluateCondition(row, clause) { + const { field, operator, value } = clause; + switch (operator) { + case '=': return row[field] === value; + case '!=': return row[field] !== value; + case '>': return row[field] > value; + case '<': return row[field] < value; + case '>=': return row[field] >= value; + case '<=': return row[field] <= value; + default: throw new Error(`Unsupported operator: ${operator}`); + } +} + module.exports = executeSELECTQuery; \ No newline at end of file diff --git a/src/queryParser.js b/src/queryParser.js index 82745d43b..55eea7d14 100644 --- a/src/queryParser.js +++ b/src/queryParser.js @@ -16,10 +16,14 @@ function parseQuery(query) { } function parseWhereClause(whereString) { - const conditions = whereString.split(/ AND | OR /i); - return conditions.map(condition => { - const [field, operator, value] = condition.split(/\s+/); - return { field, operator, value }; + const conditionRegex = /(.*?)(=|!=|>|<|>=|<=)(.*)/; + return whereString.split(/ AND | OR /i).map(conditionString => { + const match = conditionString.match(conditionRegex); + if (match) { + const [, field, operator, value] = match; + return { field: field.trim(), operator, value: value.trim() }; + } + throw new Error('Invalid WHERE clause format'); }); } From f874449de5283340afd08f1849b190c665f1cb9c Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:38:33 +0530 Subject: [PATCH 12/15] completed tutorial 8. Test cases passed: 13 --- enrollment.csv | 5 +++ src/index.js | 27 ++++++++++++++-- src/queryParser.js | 68 ++++++++++++++++++++++++++++++++------- sample.csv => student.csv | 0 4 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 enrollment.csv rename sample.csv => student.csv (100%) diff --git a/enrollment.csv b/enrollment.csv new file mode 100644 index 000000000..a81a90294 --- /dev/null +++ b/enrollment.csv @@ -0,0 +1,5 @@ +student_id,course +1,Mathematics +1,Physics +2,Chemistry +3,Mathematics diff --git a/src/index.js b/src/index.js index aba653378..ff175826c 100644 --- a/src/index.js +++ b/src/index.js @@ -2,10 +2,30 @@ const parseQuery = require('./queryParser'); const readCSV = require('./csvReader'); async function executeSELECTQuery(query) { - const { fields, table, whereClauses } = parseQuery(query); - const data = await readCSV(`${table}.csv`); + const { fields, table, whereClauses, joinTable, joinCondition } = parseQuery(query); + let data = await readCSV(`${table}.csv`); - // Apply WHERE clause filtering + // Perform INNER JOIN if specified + if (joinTable && joinCondition) { + const joinData = await readCSV(`${joinTable}.csv`); + data = data.flatMap(mainRow => { + return joinData + .filter(joinRow => { + const mainValue = mainRow[joinCondition.left.split('.')[1]]; + const joinValue = joinRow[joinCondition.right.split('.')[1]]; + return mainValue === joinValue; + }) + .map(joinRow => { + return fields.reduce((acc, field) => { + const [tableName, fieldName] = field.split('.'); + acc[field] = tableName === table ? mainRow[fieldName] : joinRow[fieldName]; + return acc; + }, {}); + }); + }); + } + + // Apply WHERE clause filtering after JOIN (or on the original data if no join) const filteredData = whereClauses.length > 0 ? data.filter(row => whereClauses.every(clause => evaluateCondition(row, clause))) : data; @@ -14,6 +34,7 @@ async function executeSELECTQuery(query) { return filteredData.map(row => { const selectedRow = {}; fields.forEach(field => { + // Assuming 'field' is just the column name without table prefix selectedRow[field] = row[field]; }); return selectedRow; diff --git a/src/queryParser.js b/src/queryParser.js index 55eea7d14..26846dfce 100644 --- a/src/queryParser.js +++ b/src/queryParser.js @@ -1,18 +1,62 @@ function parseQuery(query) { - const selectRegex = /SELECT (.+?) FROM (.+?)(?: WHERE (.*))?$/i; - const match = query.match(selectRegex); - - if (match) { - const [, fields, table, whereString] = match; - const whereClauses = whereString ? parseWhereClause(whereString) : []; - return { - fields: fields.split(',').map(field => field.trim()), - table: table.trim(), - whereClauses + // First, let's trim the query to remove any leading/trailing whitespaces + query = query.trim(); + + // Initialize variables for different parts of the query + let selectPart, fromPart; + + // Split the query at the WHERE clause if it exists + const whereSplit = query.split(/\sWHERE\s/i); + query = whereSplit[0]; // Everything before WHERE clause + + // WHERE clause is the second part after splitting, if it exists + const whereClause = whereSplit.length > 1 ? whereSplit[1].trim() : null; + + // Split the remaining query at the JOIN clause if it exists + const joinSplit = query.split(/\sINNER JOIN\s/i); + selectPart = joinSplit[0].trim(); // Everything before JOIN clause + + // JOIN clause is the second part after splitting, if it exists + const joinPart = joinSplit.length > 1 ? joinSplit[1].trim() : null; + + // Parse the SELECT part + const selectRegex = /^SELECT\s(.+?)\sFROM\s(.+)/i; + const selectMatch = selectPart.match(selectRegex); + if (!selectMatch) { + throw new Error('Invalid SELECT format'); + } + + const [, fields, table] = selectMatch; + + // Parse the JOIN part if it exists + let joinTable = null, joinCondition = null; + if (joinPart) { + const joinRegex = /^(.+?)\sON\s([\w.]+)\s*=\s*([\w.]+)/i; + const joinMatch = joinPart.match(joinRegex); + if (!joinMatch) { + throw new Error('Invalid JOIN format'); + } + + joinTable = joinMatch[1].trim(); + joinCondition = { + left: joinMatch[2].trim(), + right: joinMatch[3].trim() }; - } else { - throw new Error('Invalid query format'); } + + // Parse the WHERE part if it exists + let whereClauses = []; + if (whereClause) { + whereClauses = parseWhereClause(whereClause); + } + + return { + fields: fields.split(',').map(field => field.trim()), + table: table.trim(), + whereClauses, + joinTable, + joinCondition + }; } function parseWhereClause(whereString) { diff --git a/sample.csv b/student.csv similarity index 100% rename from sample.csv rename to student.csv From e41fe65c14ba24716a3197760d25e40d286a3a1a Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 22:54:33 +0530 Subject: [PATCH 13/15] added cli.js and queryExecutor.js --- enrollment.csv | 1 + src/cli.js | 51 +++++ src/csvReader.js | 39 ++-- src/index.js | 77 ++------ src/queryExecutor.js | 451 +++++++++++++++++++++++++++++++++++++++++++ src/queryParser.js | 252 +++++++++++++++++------- student.csv | 3 +- 7 files changed, 734 insertions(+), 140 deletions(-) create mode 100644 src/cli.js create mode 100644 src/queryExecutor.js diff --git a/enrollment.csv b/enrollment.csv index a81a90294..e80af8d93 100644 --- a/enrollment.csv +++ b/enrollment.csv @@ -3,3 +3,4 @@ student_id,course 1,Physics 2,Chemistry 3,Mathematics +5,Biology \ No newline at end of file diff --git a/src/cli.js b/src/cli.js new file mode 100644 index 000000000..458a6fe42 --- /dev/null +++ b/src/cli.js @@ -0,0 +1,51 @@ +#!/usr/bin/env node + +const readline = require ('readline'); +const { + executeSELECTQuery, + executeINSERTQuery, + executeDELETEQuery, +} = require ('./index'); + +const rl = readline.createInterface ({ + input: process.stdin, + output: process.stdout, +}); + +rl.setPrompt ('SQL> '); +console.log ( + 'SQL Query Engine CLI. Enter your SQL commands, or type "exit" to quit.' +); + +rl.prompt (); + +rl + .on ('line', async line => { + if (line.toLowerCase () === 'exit') { + rl.close (); + return; + } + + try { + if (line.toLowerCase ().startsWith ('select')) { + const result = await executeSELECTQuery (line); + console.log ('Result:', result); + } else if (line.toLowerCase ().startsWith ('insert into')) { + const result = await executeINSERTQuery (line); + console.log (result.message); + } else if (line.toLowerCase ().startsWith ('delete from')) { + const result = await executeDELETEQuery (line); + console.log (result.message); + } else { + console.log ('Unsupported command'); + } + } catch (error) { + console.error ('Error:', error.message); + } + + rl.prompt (); + }) + .on ('close', () => { + console.log ('Exiting SQL CLI'); + process.exit (0); + }); \ No newline at end of file diff --git a/src/csvReader.js b/src/csvReader.js index c39210a98..20ecfd130 100644 --- a/src/csvReader.js +++ b/src/csvReader.js @@ -1,20 +1,27 @@ -const fs = require('fs'); -const csv = require('csv-parser'); +const fs = require ('fs'); +const csv = require ('csv-parser'); +const {parse} = require ('json2csv'); -function readCSV(filePath) { - const results = []; +function readCSV (filePath) { + const results = []; - return new Promise((resolve, reject) => { - fs.createReadStream(filePath) - .pipe(csv()) - .on('data', (data) => results.push(data)) - .on('end', () => { - resolve(results); - }) - .on('error', (error) => { - reject(error); - }); - }); + return new Promise ((resolve, reject) => { + fs + .createReadStream (filePath) + .pipe (csv ()) + .on ('data', data => results.push (data)) + .on ('end', () => { + resolve (results); + }) + .on ('error', error => { + reject (error); + }); + }); } -module.exports = readCSV; \ No newline at end of file +async function writeCSV (filename, data) { + const csv = parse (data); + fs.writeFileSync (filename, csv); +} + +module.exports = {readCSV, writeCSV}; \ No newline at end of file diff --git a/src/index.js b/src/index.js index ff175826c..9cbe0d228 100644 --- a/src/index.js +++ b/src/index.js @@ -1,57 +1,22 @@ -const parseQuery = require('./queryParser'); -const readCSV = require('./csvReader'); +const {readCSV, writeCSV} = require ('./csvReader'); +const { + parseSelectQuery, + parseInsertQuery, + parseDeleteQuery, +} = require ('./queryParser'); +const { + executeSELECTQuery, + executeINSERTQuery, + executeDELETEQuery, +} = require ('./queryExecuter'); -async function executeSELECTQuery(query) { - const { fields, table, whereClauses, joinTable, joinCondition } = parseQuery(query); - let data = await readCSV(`${table}.csv`); - - // Perform INNER JOIN if specified - if (joinTable && joinCondition) { - const joinData = await readCSV(`${joinTable}.csv`); - data = data.flatMap(mainRow => { - return joinData - .filter(joinRow => { - const mainValue = mainRow[joinCondition.left.split('.')[1]]; - const joinValue = joinRow[joinCondition.right.split('.')[1]]; - return mainValue === joinValue; - }) - .map(joinRow => { - return fields.reduce((acc, field) => { - const [tableName, fieldName] = field.split('.'); - acc[field] = tableName === table ? mainRow[fieldName] : joinRow[fieldName]; - return acc; - }, {}); - }); - }); - } - - // Apply WHERE clause filtering after JOIN (or on the original data if no join) - const filteredData = whereClauses.length > 0 - ? data.filter(row => whereClauses.every(clause => evaluateCondition(row, clause))) - : data; - - // Select the specified fields - return filteredData.map(row => { - const selectedRow = {}; - fields.forEach(field => { - // Assuming 'field' is just the column name without table prefix - selectedRow[field] = row[field]; - }); - return selectedRow; - }); -} - -function evaluateCondition(row, clause) { - const { field, operator, value } = clause; - switch (operator) { - case '=': return row[field] === value; - case '!=': return row[field] !== value; - case '>': return row[field] > value; - case '<': return row[field] < value; - case '>=': return row[field] >= value; - case '<=': return row[field] <= value; - default: throw new Error(`Unsupported operator: ${operator}`); - } -} - -module.exports = executeSELECTQuery; \ No newline at end of file +module.exports = { + readCSV, + writeCSV, + executeSELECTQuery, + executeINSERTQuery, + executeDELETEQuery, + parseSelectQuery, + parseInsertQuery, + parseDeleteQuery, +}; \ No newline at end of file diff --git a/src/queryExecutor.js b/src/queryExecutor.js new file mode 100644 index 000000000..02d89e3ec --- /dev/null +++ b/src/queryExecutor.js @@ -0,0 +1,451 @@ +const { + parseSelectQuery, + parseInsertQuery, + parseDeleteQuery, +} = require('./queryParser'); +const { readCSV, writeCSV } = require('./csvReader'); + +function performInnerJoin(data, joinData, joinCondition, fields, table) { + return data.flatMap(mainRow => { + return joinData + .filter(joinRow => { + const mainValue = mainRow[joinCondition.left.split('.')[1]]; + const joinValue = joinRow[joinCondition.right.split('.')[1]]; + return mainValue === joinValue; + }) + .map(joinRow => { + return fields.reduce((acc, field) => { + const [tableName, fieldName] = field.split('.'); + acc[field] = tableName === table + ? mainRow[fieldName] + : joinRow[fieldName]; + return acc; + }, {}); + }); + }); +} + +function performLeftJoin(data, joinData, joinCondition, fields, table) { + return data.flatMap(mainRow => { + const matchingJoinRows = joinData.filter(joinRow => { + const mainValue = getValueFromRow(mainRow, joinCondition.left); + const joinValue = getValueFromRow(joinRow, joinCondition.right); + return mainValue === joinValue; + }); + + if (matchingJoinRows.length === 0) { + return [createResultRow(mainRow, null, fields, table, true)]; + } + + return matchingJoinRows.map(joinRow => + createResultRow(mainRow, joinRow, fields, table, true) + ); + }); +} + +function getValueFromRow(row, compoundFieldName) { + const [tableName, fieldName] = compoundFieldName.split('.'); + return row[`${tableName}.${fieldName}`] || row[fieldName]; +} + +function performRightJoin(data, joinData, joinCondition, fields, table) { + // Cache the structure of a main table row (keys only) + const mainTableRowStructure = data.length > 0 + ? Object.keys(data[0]).reduce((acc, key) => { + acc[key] = null; // Set all values to null initially + return acc; + }, {}) + : {}; + + return joinData.map(joinRow => { + const mainRowMatch = data.find(mainRow => { + const mainValue = getValueFromRow(mainRow, joinCondition.left); + const joinValue = getValueFromRow(joinRow, joinCondition.right); + return mainValue === joinValue; + }); + + // Use the cached structure if no match is found + const mainRowToUse = mainRowMatch || mainTableRowStructure; + + // Include all necessary fields from the 'student' table + return createResultRow(mainRowToUse, joinRow, fields, table, true); + }); +} + +function createResultRow( + mainRow, + joinRow, + fields, + table, + includeAllMainFields +) { + const resultRow = {}; + + if (includeAllMainFields) { + // Include all fields from the main table + Object.keys(mainRow || {}).forEach(key => { + const prefixedKey = `${table}.${key}`; + resultRow[prefixedKey] = mainRow ? mainRow[key] : null; + }); + } + + // Now, add or overwrite with the fields specified in the query + fields.forEach(field => { + const [tableName, fieldName] = field.includes('.') + ? field.split('.') + : [table, field]; + resultRow[field] = tableName === table && mainRow + ? mainRow[fieldName] + : joinRow ? joinRow[fieldName] : null; + }); + + return resultRow; +} + +function evaluateCondition(row, clause) { + let { field, operator, value } = clause; + + // Check if the field exists in the row + if (row[field] === undefined) { + throw new Error(`Invalid field: ${field}`); + } + + // Parse row value and condition value based on their actual types + const rowValue = parseValue(row[field]); + let conditionValue = parseValue(value); + + if (operator === 'LIKE') { + // Transform SQL LIKE pattern to JavaScript RegExp pattern + const regexPattern = + '^' + value.replace(/%/g, '.*').replace(/_/g, '.') + '$'; + const regex = new RegExp(regexPattern, 'i'); // 'i' for case-insensitive matching + return regex.test(row[field]); + } + + switch (operator) { + case '=': + return rowValue === conditionValue; + case '!=': + return rowValue !== conditionValue; + case '>': + return rowValue > conditionValue; + case '<': + return rowValue < conditionValue; + case '>=': + return rowValue >= conditionValue; + case '<=': + return rowValue <= conditionValue; + default: + throw new Error(`Unsupported operator: ${operator}`); + } +} + +// Helper function to parse value based on its apparent type +function parseValue(value) { + // Return null or undefined as is + if (value === null || value === undefined) { + return value; + } + + // If the value is a string enclosed in single or double quotes, remove them + if ( + typeof value === 'string' && + ((value.startsWith("'") && value.endsWith("'")) || + (value.startsWith('"') && value.endsWith('"'))) + ) { + value = value.substring(1, value.length - 1); + } + + // Check if value is a number + if (!isNaN(value) && value.trim() !== '') { + return Number(value); + } + // Assume value is a string if not a number + return value; +} + +function applyGroupBy(data, groupByFields, aggregateFunctions) { + const groupResults = {}; + + data.forEach(row => { + // Generate a key for the group + const groupKey = groupByFields.map(field => row[field]).join('-'); + + // Initialize group in results if it doesn't exist + if (!groupResults[groupKey]) { + groupResults[groupKey] = { count: 0, sums: {}, mins: {}, maxes: {} }; + groupByFields.forEach( + field => (groupResults[groupKey][field] = row[field]) + ); + } + + // Aggregate calculations + groupResults[groupKey].count += 1; + aggregateFunctions.forEach(func => { + const match = /(\w+)\((\w+)\)/.exec(func); + if (match) { + const [, aggFunc, aggField] = match; + const value = parseFloat(row[aggField]); + + switch (aggFunc.toUpperCase()) { + case 'SUM': + groupResults[groupKey].sums[aggField] = + (groupResults[groupKey].sums[aggField] || 0) + value; + break; + case 'MIN': + groupResults[groupKey].mins[aggField] = Math.min( + groupResults[groupKey].mins[aggField] || value, + value + ); + break; + case 'MAX': + groupResults[groupKey].maxes[aggField] = Math.max( + groupResults[groupKey].maxes[aggField] || value, + value + ); + break; + // Additional aggregate functions can be added here + } + } + }); + }); + + // Convert grouped results into an array format + return Object.values(groupResults).map(group => { + // Construct the final grouped object based on required fields + const finalGroup = {}; + groupByFields.forEach(field => (finalGroup[field] = group[field])); + aggregateFunctions.forEach(func => { + const match = /(\w+)\((\*|\w+)\)/.exec(func); + if (match) { + const [, aggFunc, aggField] = match; + switch (aggFunc.toUpperCase()) { + case 'SUM': + finalGroup[func] = group.sums[aggField]; + break; + case 'MIN': + finalGroup[func] = group.mins[aggField]; + break; + case 'MAX': + finalGroup[func] = group.maxes[aggField]; + break; + case 'COUNT': + finalGroup[func] = group.count; + break; + // Additional aggregate functions can be handled here + } + } + }); + + return finalGroup; + }); +} + +async function executeSELECTQuery(query) { + try { + const { + fields, + table, + whereClauses, + joinType, + joinTable, + joinCondition, + groupByFields, + hasAggregateWithoutGroupBy, + orderByFields, + limit, + isDistinct, + } = parseSelectQuery(query); + let data = await readCSV(`${table}.csv`); + + // Perform INNER JOIN if specified + if (joinTable && joinCondition) { + const joinData = await readCSV(`${joinTable}.csv`); + switch (joinType.toUpperCase()) { + case 'INNER': + data = performInnerJoin( + data, + joinData, + joinCondition, + fields, + table + ); + break; + case 'LEFT': + data = performLeftJoin(data, joinData, joinCondition, fields, table); + break; + case 'RIGHT': + data = performRightJoin( + data, + joinData, + joinCondition, + fields, + table + ); + break; + default: + throw new Error(`Unsupported JOIN type: ${joinType}`); + } + } + // Apply WHERE clause filtering after JOIN (or on the original data if no join) + let filteredData = whereClauses.length > 0 + ? data.filter(row => + whereClauses.every(clause => evaluateCondition(row, clause)) + ) + : data; + + let groupResults = filteredData; + if (hasAggregateWithoutGroupBy) { + // Special handling for queries like 'SELECT COUNT(*) FROM table' + const result = {}; + + fields.forEach(field => { + const match = /(\w+)\((\*|\w+)\)/.exec(field); + if (match) { + const [, aggFunc, aggField] = match; + switch (aggFunc.toUpperCase()) { + case 'COUNT': + result[field] = filteredData.length; + break; + case 'SUM': + result[field] = filteredData.reduce( + (acc, row) => acc + parseFloat(row[aggField]), + 0 + ); + break; + case 'AVG': + result[field] = + filteredData.reduce( + (acc, row) => acc + parseFloat(row[aggField]), + 0 + ) / filteredData.length; + break; + case 'MIN': + result[field] = Math.min( + ...filteredData.map(row => parseFloat(row[aggField])) + ); + break; + case 'MAX': + result[field] = Math.max( + ...filteredData.map(row => parseFloat(row[aggField])) + ); + break; + // Additional aggregate functions can be handled here + } + } + }); + + return [result]; + // Add more cases here if needed for other aggregates + } else if (groupByFields) { + groupResults = applyGroupBy(filteredData, groupByFields, fields); + + // Order them by the specified fields + let orderedResults = groupResults; + if (orderByFields) { + orderedResults = groupResults.sort((a, b) => { + for (let { fieldName, order } of orderByFields) { + if (a[fieldName] < b[fieldName]) return order === 'ASC' ? -1 : 1; + if (a[fieldName] > b[fieldName]) return order === 'ASC' ? 1 : -1; + } + return 0; + }); + } + if (limit !== null) { + groupResults = groupResults.slice(0, limit); + } + return groupResults; + } else { + // Order them by the specified fields + let orderedResults = groupResults; + if (orderByFields) { + orderedResults = groupResults.sort((a, b) => { + for (let { fieldName, order } of orderByFields) { + if (a[fieldName] < b[fieldName]) return order === 'ASC' ? -1 : 1; + if (a[fieldName] > b[fieldName]) return order === 'ASC' ? 1 : -1; + } + return 0; + }); + } + + // Select the specified fields + let finalResults = orderedResults.map(row => { + const selectedRow = {}; + fields.forEach(field => { + // Assuming 'field' is just the column name without table prefix + selectedRow[field] = row[field]; + }); + return selectedRow; + }); + + // Remove duplicates if specified + let distinctResults = finalResults; + if (isDistinct) { + distinctResults = [ + ...new Map( + finalResults.map(item => [ + fields.map(field => item[field]).join('|'), + item, + ]) + ).values(), + ]; + } + + let limitResults = distinctResults; + if (limit !== null) { + limitResults = distinctResults.slice(0, limit); + } + + return limitResults; + } + } catch (error) { + throw new Error(`Error executing query: ${error.message}`); + } +} + +async function executeINSERTQuery(query) { + console.log(parseInsertQuery(query)); + const { table, columns, values } = parseInsertQuery(query); + const data = await readCSV(`${table}.csv`); + + // Create a new row object + const newRow = {}; + columns.forEach((column, index) => { + // Remove single quotes from the values + let value = values[index]; + if (value.startsWith("'") && value.endsWith("'")) { + value = value.substring(1, value.length - 1); + } + newRow[column] = value; + }); + + // Add the new row to the data + data.push(newRow); + + // Save the updated data back to the CSV file + await writeCSV(`${table}.csv`, data); // Implement writeCSV function + + return { message: 'Row inserted successfully.' }; +} + +async function executeDELETEQuery(query) { + const { table, whereClauses } = parseDeleteQuery(query); + let data = await readCSV(`${table}.csv`); + + if (whereClauses.length > 0) { + // Filter out the rows that meet the where clause conditions + data = data.filter( + row => !whereClauses.every(clause => evaluateCondition(row, clause)) + ); + } else { + // If no where clause, clear the entire table + data = []; + } + + // Save the updated data back to the CSV file + await writeCSV(`${table}.csv`, data); + + return { message: 'Rows deleted successfully.' }; +} + +module.exports = { executeSELECTQuery, executeINSERTQuery, executeDELETEQuery }; \ No newline at end of file diff --git a/src/queryParser.js b/src/queryParser.js index 26846dfce..95bd0245d 100644 --- a/src/queryParser.js +++ b/src/queryParser.js @@ -1,74 +1,192 @@ -function parseQuery(query) { - // First, let's trim the query to remove any leading/trailing whitespaces - query = query.trim(); - - // Initialize variables for different parts of the query - let selectPart, fromPart; - - // Split the query at the WHERE clause if it exists - const whereSplit = query.split(/\sWHERE\s/i); - query = whereSplit[0]; // Everything before WHERE clause - - // WHERE clause is the second part after splitting, if it exists - const whereClause = whereSplit.length > 1 ? whereSplit[1].trim() : null; - - // Split the remaining query at the JOIN clause if it exists - const joinSplit = query.split(/\sINNER JOIN\s/i); - selectPart = joinSplit[0].trim(); // Everything before JOIN clause - - // JOIN clause is the second part after splitting, if it exists - const joinPart = joinSplit.length > 1 ? joinSplit[1].trim() : null; - - // Parse the SELECT part - const selectRegex = /^SELECT\s(.+?)\sFROM\s(.+)/i; - const selectMatch = selectPart.match(selectRegex); - if (!selectMatch) { - throw new Error('Invalid SELECT format'); +/* +Creating a Query Parser which can parse SQL `SELECT` Queries only. +// */ +function parseSelectQuery (query) { + try { + // Trim the query to remove any leading/trailing whitespaces + query = query.trim (); + + // Initialize distinct flag + let isDistinct = false; + + // Check for DISTINCT keyword and update the query + if (query.toUpperCase ().includes ('SELECT DISTINCT')) { + isDistinct = true; + query = query.replace ('SELECT DISTINCT', 'SELECT'); + } + + // Updated regex to capture LIMIT clause and remove it for further processing + const limitRegex = /\sLIMIT\s(\d+)/i; + const limitMatch = query.match (limitRegex); + + let limit = null; + if (limitMatch) { + limit = parseInt (limitMatch[1], 10); + query = query.replace (limitRegex, ''); // Remove LIMIT clause + } + + // Process ORDER BY clause and remove it for further processing + const orderByRegex = /\sORDER BY\s(.+)/i; + const orderByMatch = query.match (orderByRegex); + let orderByFields = null; + if (orderByMatch) { + orderByFields = orderByMatch[1].split (',').map (field => { + const [fieldName, order] = field.trim ().split (/\s+/); + return {fieldName, order: order ? order.toUpperCase () : 'ASC'}; + }); + query = query.replace (orderByRegex, ''); + } + + // Process GROUP BY clause and remove it for further processing + const groupByRegex = /\sGROUP BY\s(.+)/i; + const groupByMatch = query.match (groupByRegex); + let groupByFields = null; + if (groupByMatch) { + groupByFields = groupByMatch[1].split (',').map (field => field.trim ()); + query = query.replace (groupByRegex, ''); + } + + // Process WHERE clause + const whereSplit = query.split (/\sWHERE\s/i); + const queryWithoutWhere = whereSplit[0]; // Everything before WHERE clause + const whereClause = whereSplit.length > 1 ? whereSplit[1].trim () : null; + + // Process JOIN clause + const joinSplit = queryWithoutWhere.split (/\s(INNER|LEFT|RIGHT) JOIN\s/i); + const selectPart = joinSplit[0].trim (); // Everything before JOIN clause + + // Extract JOIN information + const {joinType, joinTable, joinCondition} = parseJoinClause ( + queryWithoutWhere + ); + + // Parse SELECT part + const selectRegex = /^SELECT\s(.+?)\sFROM\s(.+)/i; + const selectMatch = selectPart.match (selectRegex); + if (!selectMatch) { + throw new Error ('Invalid SELECT format'); + } + const [, fields, table] = selectMatch; + + // Parse WHERE part if it exists + let whereClauses = []; + if (whereClause) { + whereClauses = parseWhereClause (whereClause); + } + + // Check for aggregate functions without GROUP BY + const hasAggregateWithoutGroupBy = checkAggregateWithoutGroupBy ( + query, + groupByFields + ); + + return { + fields: fields.split (',').map (field => field.trim ()), + table: table.trim (), + whereClauses, + joinType, + joinTable, + joinCondition, + groupByFields, + orderByFields, + hasAggregateWithoutGroupBy, + limit, + isDistinct, + }; + } catch (error) { + throw new Error (`Query parsing error: ${error.message}`); } - - const [, fields, table] = selectMatch; - - // Parse the JOIN part if it exists - let joinTable = null, joinCondition = null; - if (joinPart) { - const joinRegex = /^(.+?)\sON\s([\w.]+)\s*=\s*([\w.]+)/i; - const joinMatch = joinPart.match(joinRegex); - if (!joinMatch) { - throw new Error('Invalid JOIN format'); - } - - joinTable = joinMatch[1].trim(); - joinCondition = { - left: joinMatch[2].trim(), - right: joinMatch[3].trim() + } + + function checkAggregateWithoutGroupBy (query, groupByFields) { + const aggregateFunctionRegex = /(\bCOUNT\b|\bAVG\b|\bSUM\b|\bMIN\b|\bMAX\b)\s*\(\s*(\*|\w+)\s*\)/i; + return aggregateFunctionRegex.test (query) && !groupByFields; + } + + function parseWhereClause (whereString) { + const conditionRegex = /(.*?)(=|!=|>=|<=|>|<)(.*)/; + return whereString.split (/ AND | OR /i).map (conditionString => { + if (conditionString.includes (' LIKE ')) { + const [field, pattern] = conditionString.split (/\sLIKE\s/i); + return { + field: field.trim (), + operator: 'LIKE', + value: pattern.trim ().replace (/^'(.*)'$/, '$1'), }; + } else { + const match = conditionString.match (conditionRegex); + if (match) { + const [, field, operator, value] = match; + return {field: field.trim (), operator, value: value.trim ()}; + } + throw new Error ('Invalid WHERE clause format'); + } + }); + } + + function parseJoinClause (query) { + const joinRegex = /\s(INNER|LEFT|RIGHT) JOIN\s(.+?)\sON\s([\w.]+)\s*=\s*([\w.]+)/i; + const joinMatch = query.match (joinRegex); + + if (joinMatch) { + return { + joinType: joinMatch[1].trim (), + joinTable: joinMatch[2].trim (), + joinCondition: { + left: joinMatch[3].trim (), + right: joinMatch[4].trim (), + }, + }; + } + + return { + joinType: null, + joinTable: null, + joinCondition: null, + }; + } + + function parseInsertQuery (query) { + const insertRegex = /INSERT INTO (\w+)\s\((.+)\)\sVALUES\s\((.+)\)/i; + const match = query.match (insertRegex); + + if (!match) { + throw new Error ('Invalid INSERT INTO syntax.'); } - - // Parse the WHERE part if it exists + + const [, table, columns, values] = match; + return { + type: 'INSERT', + table: table.trim (), + columns: columns.split (',').map (column => column.trim ()), + values: values.split (',').map (value => value.trim ()), + }; + } + + function parseDeleteQuery (query) { + const deleteRegex = /DELETE FROM (\w+)( WHERE (.*))?/i; + const match = query.match (deleteRegex); + + if (!match) { + throw new Error ('Invalid DELETE syntax.'); + } + + const [, table, , whereString] = match; let whereClauses = []; - if (whereClause) { - whereClauses = parseWhereClause(whereClause); + if (whereString) { + whereClauses = parseWhereClause (whereString); } - + return { - fields: fields.split(',').map(field => field.trim()), - table: table.trim(), - whereClauses, - joinTable, - joinCondition + type: 'DELETE', + table: table.trim (), + whereClauses, }; -} - -function parseWhereClause(whereString) { - const conditionRegex = /(.*?)(=|!=|>|<|>=|<=)(.*)/; - return whereString.split(/ AND | OR /i).map(conditionString => { - const match = conditionString.match(conditionRegex); - if (match) { - const [, field, operator, value] = match; - return { field: field.trim(), operator, value: value.trim() }; - } - throw new Error('Invalid WHERE clause format'); - }); -} - -module.exports = parseQuery; \ No newline at end of file + } + + module.exports = { + parseSelectQuery, + parseJoinClause, + parseInsertQuery, + parseDeleteQuery, + }; \ No newline at end of file diff --git a/student.csv b/student.csv index 9e7a9fa25..e9c960121 100644 --- a/student.csv +++ b/student.csv @@ -1,4 +1,5 @@ id,name,age 1,John,30 2,Jane,25 -3,Bob,22 \ No newline at end of file +3,Bob,22 +4,Alice,24 \ No newline at end of file From 79f2538c221813e30288efeb72031869d9dd01bd Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sat, 4 May 2024 23:10:44 +0530 Subject: [PATCH 14/15] assignment finished with all test cases passed --- src/cli.js | 116 ++++--- src/csvReader.js | 57 ++-- src/index.js | 32 +- src/queryExecutor.js | 165 +++------ src/queryParser.js | 315 +++++++++--------- tests/step-02/index.test.js | 6 +- tests/step-03/index.test.js | 24 +- tests/step-04/index.test.js | 27 +- tests/step-05/index.test.js | 52 ++- tests/step-06/index.test.js | 72 ++-- tests/step-07/index.test.js | 81 +++-- tests/step-08/index.test.js | 65 ++-- tests/step-09/index.test.js | 54 ++- tests/step-10/index.test.js | 114 +++++-- tests/step-11/index.test.js | 126 ++++--- tests/step-12/index.test.js | 104 +++--- tests/step-13/index.test.js | 104 +++--- tests/step-14/index.test.js | 46 +-- tests/step-15/index.test.js | 46 +-- tests/step-16/index.test.js | 46 +-- tests/step-17/index.test.js | 2 +- tests/step-17/insertExecuter.test.js | 3 +- tests/step-18/deleteExecutor.test.js | 2 +- tests/step-18/index.test.js | 2 +- tests/step-18/insertExecuter.test.js | 2 +- ...t.js_344b632c9c4863f3843be516d9be4761.prob | 1 + tests/step-19/deleteExecutor.test.js | 2 +- tests/step-19/index.test.js | 2 +- tests/step-19/insertExecuter.test.js | 89 +++-- 29 files changed, 1008 insertions(+), 749 deletions(-) create mode 100644 tests/step-19/.cph/.insertExecuter.test.js_344b632c9c4863f3843be516d9be4761.prob diff --git a/src/cli.js b/src/cli.js index 458a6fe42..6d281e6ea 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,51 +1,89 @@ #!/usr/bin/env node -const readline = require ('readline'); -const { - executeSELECTQuery, - executeINSERTQuery, - executeDELETEQuery, -} = require ('./index'); - -const rl = readline.createInterface ({ - input: process.stdin, - output: process.stdout, +const readline = require('readline'); + +const { executeSELECTQuery, executeINSERTQuery, executeDELETEQuery } = require('./index'); + + + + +const rl = readline.createInterface({ + + input: process.stdin, + + output: process.stdout + }); -rl.setPrompt ('SQL> '); -console.log ( - 'SQL Query Engine CLI. Enter your SQL commands, or type "exit" to quit.' -); -rl.prompt (); -rl - .on ('line', async line => { - if (line.toLowerCase () === 'exit') { - rl.close (); - return; + +rl.setPrompt('SQL> '); + +console.log('SQL Query Engine CLI. Enter your SQL commands, or type "exit" to quit.'); + + + + +rl.prompt(); + + + + +rl.on('line', async (line) => { + + if (line.toLowerCase() === 'exit') { + + rl.close(); + + return; + } + + + try { - if (line.toLowerCase ().startsWith ('select')) { - const result = await executeSELECTQuery (line); - console.log ('Result:', result); - } else if (line.toLowerCase ().startsWith ('insert into')) { - const result = await executeINSERTQuery (line); - console.log (result.message); - } else if (line.toLowerCase ().startsWith ('delete from')) { - const result = await executeDELETEQuery (line); - console.log (result.message); - } else { - console.log ('Unsupported command'); - } + + if (line.toLowerCase().startsWith('select')) { + + const result = await executeSELECTQuery(line); + + console.log('Result:', result); + + } else if (line.toLowerCase().startsWith('insert into')) { + + const result = await executeINSERTQuery(line); + + console.log(result.message); + + } else if (line.toLowerCase().startsWith('delete from')) { + + const result = await executeDELETEQuery(line); + + console.log(result.message); + + } else { + + console.log('Unsupported command'); + + } + } catch (error) { - console.error ('Error:', error.message); + + console.error('Error:', error.message); + } - rl.prompt (); - }) - .on ('close', () => { - console.log ('Exiting SQL CLI'); - process.exit (0); - }); \ No newline at end of file + + + + rl.prompt(); + +}).on('close', () => { + + console.log('Exiting SQL CLI'); + + process.exit(0); + +}); \ No newline at end of file diff --git a/src/csvReader.js b/src/csvReader.js index 20ecfd130..1296d8a25 100644 --- a/src/csvReader.js +++ b/src/csvReader.js @@ -1,27 +1,42 @@ -const fs = require ('fs'); -const csv = require ('csv-parser'); -const {parse} = require ('json2csv'); +const fs = require('fs'); +const csv = require('csv-parser'); +const { parse } = require('json2csv'); -function readCSV (filePath) { - const results = []; +// function readCSV(filePath) { +// const results = []; - return new Promise ((resolve, reject) => { - fs - .createReadStream (filePath) - .pipe (csv ()) - .on ('data', data => results.push (data)) - .on ('end', () => { - resolve (results); - }) - .on ('error', error => { - reject (error); - }); - }); +// return new Promise((resolve, reject) => { +// fs.createReadStream(filePath) +// .pipe(csv()) +// .on('data', (data) => results.push(data)) +// .on('end', () => { +// resolve(results); +// }) +// .on('error', (error) => { +// reject(error); +// }); +// }); +// } +function readCSV(filePath) { + const results = []; + return new Promise((resolve, reject) => { + fs.createReadStream(filePath) + .pipe(csv()) + .on('data', (data) => { + results.push(data) + }) + .on('end', () => { + resolve(results); + }) + .on('error', (error) => { + reject(error); + }); + }); } -async function writeCSV (filename, data) { - const csv = parse (data); - fs.writeFileSync (filename, csv); +async function writeCSV(filename, data) { + const csv = parse(data); + fs.writeFileSync(filename, csv); } -module.exports = {readCSV, writeCSV}; \ No newline at end of file +module.exports = { readCSV, writeCSV }; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 9cbe0d228..37946433b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,22 +1,14 @@ -const {readCSV, writeCSV} = require ('./csvReader'); -const { - parseSelectQuery, - parseInsertQuery, - parseDeleteQuery, -} = require ('./queryParser'); -const { - executeSELECTQuery, - executeINSERTQuery, - executeDELETEQuery, -} = require ('./queryExecuter'); +const { readCSV, writeCSV } = require('./csvReader'); +const { parseSelectQuery, parseInsertQuery, parseDeleteQuery } = require('./queryParser'); +const { executeSELECTQuery, executeINSERTQuery, executeDELETEQuery } = require('./queryExecuter'); module.exports = { - readCSV, - writeCSV, - executeSELECTQuery, - executeINSERTQuery, - executeDELETEQuery, - parseSelectQuery, - parseInsertQuery, - parseDeleteQuery, -}; \ No newline at end of file + readCSV, + writeCSV, + executeSELECTQuery, + executeINSERTQuery, + executeDELETEQuery, + parseSelectQuery, + parseInsertQuery, + parseDeleteQuery +} \ No newline at end of file diff --git a/src/queryExecutor.js b/src/queryExecutor.js index 02d89e3ec..85681f54b 100644 --- a/src/queryExecutor.js +++ b/src/queryExecutor.js @@ -1,8 +1,4 @@ -const { - parseSelectQuery, - parseInsertQuery, - parseDeleteQuery, -} = require('./queryParser'); +const { parseSelectQuery, parseInsertQuery, parseDeleteQuery } = require('./queryParser'); const { readCSV, writeCSV } = require('./csvReader'); function performInnerJoin(data, joinData, joinCondition, fields, table) { @@ -16,9 +12,7 @@ function performInnerJoin(data, joinData, joinCondition, fields, table) { .map(joinRow => { return fields.reduce((acc, field) => { const [tableName, fieldName] = field.split('.'); - acc[field] = tableName === table - ? mainRow[fieldName] - : joinRow[fieldName]; + acc[field] = tableName === table ? mainRow[fieldName] : joinRow[fieldName]; return acc; }, {}); }); @@ -37,9 +31,7 @@ function performLeftJoin(data, joinData, joinCondition, fields, table) { return [createResultRow(mainRow, null, fields, table, true)]; } - return matchingJoinRows.map(joinRow => - createResultRow(mainRow, joinRow, fields, table, true) - ); + return matchingJoinRows.map(joinRow => createResultRow(mainRow, joinRow, fields, table, true)); }); } @@ -50,12 +42,10 @@ function getValueFromRow(row, compoundFieldName) { function performRightJoin(data, joinData, joinCondition, fields, table) { // Cache the structure of a main table row (keys only) - const mainTableRowStructure = data.length > 0 - ? Object.keys(data[0]).reduce((acc, key) => { - acc[key] = null; // Set all values to null initially - return acc; - }, {}) - : {}; + const mainTableRowStructure = data.length > 0 ? Object.keys(data[0]).reduce((acc, key) => { + acc[key] = null; // Set all values to null initially + return acc; + }, {}) : {}; return joinData.map(joinRow => { const mainRowMatch = data.find(mainRow => { @@ -72,13 +62,7 @@ function performRightJoin(data, joinData, joinCondition, fields, table) { }); } -function createResultRow( - mainRow, - joinRow, - fields, - table, - includeAllMainFields -) { +function createResultRow(mainRow, joinRow, fields, table, includeAllMainFields) { const resultRow = {}; if (includeAllMainFields) { @@ -91,12 +75,8 @@ function createResultRow( // Now, add or overwrite with the fields specified in the query fields.forEach(field => { - const [tableName, fieldName] = field.includes('.') - ? field.split('.') - : [table, field]; - resultRow[field] = tableName === table && mainRow - ? mainRow[fieldName] - : joinRow ? joinRow[fieldName] : null; + const [tableName, fieldName] = field.includes('.') ? field.split('.') : [table, field]; + resultRow[field] = tableName === table && mainRow ? mainRow[fieldName] : joinRow ? joinRow[fieldName] : null; }); return resultRow; @@ -116,43 +96,32 @@ function evaluateCondition(row, clause) { if (operator === 'LIKE') { // Transform SQL LIKE pattern to JavaScript RegExp pattern - const regexPattern = - '^' + value.replace(/%/g, '.*').replace(/_/g, '.') + '$'; + const regexPattern = '^' + value.replace(/%/g, '.*').replace(/_/g, '.') + '$'; const regex = new RegExp(regexPattern, 'i'); // 'i' for case-insensitive matching return regex.test(row[field]); } switch (operator) { - case '=': - return rowValue === conditionValue; - case '!=': - return rowValue !== conditionValue; - case '>': - return rowValue > conditionValue; - case '<': - return rowValue < conditionValue; - case '>=': - return rowValue >= conditionValue; - case '<=': - return rowValue <= conditionValue; - default: - throw new Error(`Unsupported operator: ${operator}`); + case '=': return rowValue === conditionValue; + case '!=': return rowValue !== conditionValue; + case '>': return rowValue > conditionValue; + case '<': return rowValue < conditionValue; + case '>=': return rowValue >= conditionValue; + case '<=': return rowValue <= conditionValue; + default: throw new Error(`Unsupported operator: ${operator}`); } } // Helper function to parse value based on its apparent type function parseValue(value) { + // Return null or undefined as is if (value === null || value === undefined) { return value; } // If the value is a string enclosed in single or double quotes, remove them - if ( - typeof value === 'string' && - ((value.startsWith("'") && value.endsWith("'")) || - (value.startsWith('"') && value.endsWith('"'))) - ) { + if (typeof value === 'string' && ((value.startsWith("'") && value.endsWith("'")) || (value.startsWith('"') && value.endsWith('"')))) { value = value.substring(1, value.length - 1); } @@ -174,9 +143,7 @@ function applyGroupBy(data, groupByFields, aggregateFunctions) { // Initialize group in results if it doesn't exist if (!groupResults[groupKey]) { groupResults[groupKey] = { count: 0, sums: {}, mins: {}, maxes: {} }; - groupByFields.forEach( - field => (groupResults[groupKey][field] = row[field]) - ); + groupByFields.forEach(field => groupResults[groupKey][field] = row[field]); } // Aggregate calculations @@ -189,20 +156,13 @@ function applyGroupBy(data, groupByFields, aggregateFunctions) { switch (aggFunc.toUpperCase()) { case 'SUM': - groupResults[groupKey].sums[aggField] = - (groupResults[groupKey].sums[aggField] || 0) + value; + groupResults[groupKey].sums[aggField] = (groupResults[groupKey].sums[aggField] || 0) + value; break; case 'MIN': - groupResults[groupKey].mins[aggField] = Math.min( - groupResults[groupKey].mins[aggField] || value, - value - ); + groupResults[groupKey].mins[aggField] = Math.min(groupResults[groupKey].mins[aggField] || value, value); break; case 'MAX': - groupResults[groupKey].maxes[aggField] = Math.max( - groupResults[groupKey].maxes[aggField] || value, - value - ); + groupResults[groupKey].maxes[aggField] = Math.max(groupResults[groupKey].maxes[aggField] || value, value); break; // Additional aggregate functions can be added here } @@ -214,7 +174,7 @@ function applyGroupBy(data, groupByFields, aggregateFunctions) { return Object.values(groupResults).map(group => { // Construct the final grouped object based on required fields const finalGroup = {}; - groupByFields.forEach(field => (finalGroup[field] = group[field])); + groupByFields.forEach(field => finalGroup[field] = group[field]); aggregateFunctions.forEach(func => { const match = /(\w+)\((\*|\w+)\)/.exec(func); if (match) { @@ -243,19 +203,8 @@ function applyGroupBy(data, groupByFields, aggregateFunctions) { async function executeSELECTQuery(query) { try { - const { - fields, - table, - whereClauses, - joinType, - joinTable, - joinCondition, - groupByFields, - hasAggregateWithoutGroupBy, - orderByFields, - limit, - isDistinct, - } = parseSelectQuery(query); + + const { fields, table, whereClauses, joinType, joinTable, joinCondition, groupByFields, hasAggregateWithoutGroupBy, orderByFields, limit, isDistinct } = parseSelectQuery(query); let data = await readCSV(`${table}.csv`); // Perform INNER JOIN if specified @@ -263,25 +212,13 @@ async function executeSELECTQuery(query) { const joinData = await readCSV(`${joinTable}.csv`); switch (joinType.toUpperCase()) { case 'INNER': - data = performInnerJoin( - data, - joinData, - joinCondition, - fields, - table - ); + data = performInnerJoin(data, joinData, joinCondition, fields, table); break; case 'LEFT': data = performLeftJoin(data, joinData, joinCondition, fields, table); break; case 'RIGHT': - data = performRightJoin( - data, - joinData, - joinCondition, - fields, - table - ); + data = performRightJoin(data, joinData, joinCondition, fields, table); break; default: throw new Error(`Unsupported JOIN type: ${joinType}`); @@ -289,9 +226,7 @@ async function executeSELECTQuery(query) { } // Apply WHERE clause filtering after JOIN (or on the original data if no join) let filteredData = whereClauses.length > 0 - ? data.filter(row => - whereClauses.every(clause => evaluateCondition(row, clause)) - ) + ? data.filter(row => whereClauses.every(clause => evaluateCondition(row, clause))) : data; let groupResults = filteredData; @@ -308,27 +243,16 @@ async function executeSELECTQuery(query) { result[field] = filteredData.length; break; case 'SUM': - result[field] = filteredData.reduce( - (acc, row) => acc + parseFloat(row[aggField]), - 0 - ); + result[field] = filteredData.reduce((acc, row) => acc + parseFloat(row[aggField]), 0); break; case 'AVG': - result[field] = - filteredData.reduce( - (acc, row) => acc + parseFloat(row[aggField]), - 0 - ) / filteredData.length; + result[field] = filteredData.reduce((acc, row) => acc + parseFloat(row[aggField]), 0) / filteredData.length; break; case 'MIN': - result[field] = Math.min( - ...filteredData.map(row => parseFloat(row[aggField])) - ); + result[field] = Math.min(...filteredData.map(row => parseFloat(row[aggField]))); break; case 'MAX': - result[field] = Math.max( - ...filteredData.map(row => parseFloat(row[aggField])) - ); + result[field] = Math.max(...filteredData.map(row => parseFloat(row[aggField]))); break; // Additional aggregate functions can be handled here } @@ -356,6 +280,7 @@ async function executeSELECTQuery(query) { } return groupResults; } else { + // Order them by the specified fields let orderedResults = groupResults; if (orderByFields) { @@ -381,14 +306,7 @@ async function executeSELECTQuery(query) { // Remove duplicates if specified let distinctResults = finalResults; if (isDistinct) { - distinctResults = [ - ...new Map( - finalResults.map(item => [ - fields.map(field => item[field]).join('|'), - item, - ]) - ).values(), - ]; + distinctResults = [...new Map(finalResults.map(item => [fields.map(field => item[field]).join('|'), item])).values()]; } let limitResults = distinctResults; @@ -397,6 +315,8 @@ async function executeSELECTQuery(query) { } return limitResults; + + } } catch (error) { throw new Error(`Error executing query: ${error.message}`); @@ -425,7 +345,7 @@ async function executeINSERTQuery(query) { // Save the updated data back to the CSV file await writeCSV(`${table}.csv`, data); // Implement writeCSV function - return { message: 'Row inserted successfully.' }; + return { message: "Row inserted successfully." }; } async function executeDELETEQuery(query) { @@ -434,9 +354,7 @@ async function executeDELETEQuery(query) { if (whereClauses.length > 0) { // Filter out the rows that meet the where clause conditions - data = data.filter( - row => !whereClauses.every(clause => evaluateCondition(row, clause)) - ); + data = data.filter(row => !whereClauses.every(clause => evaluateCondition(row, clause))); } else { // If no where clause, clear the entire table data = []; @@ -445,7 +363,8 @@ async function executeDELETEQuery(query) { // Save the updated data back to the CSV file await writeCSV(`${table}.csv`, data); - return { message: 'Rows deleted successfully.' }; + return { message: "Rows deleted successfully." }; } + module.exports = { executeSELECTQuery, executeINSERTQuery, executeDELETEQuery }; \ No newline at end of file diff --git a/src/queryParser.js b/src/queryParser.js index 95bd0245d..67b94f5d0 100644 --- a/src/queryParser.js +++ b/src/queryParser.js @@ -1,192 +1,177 @@ -/* -Creating a Query Parser which can parse SQL `SELECT` Queries only. -// */ -function parseSelectQuery (query) { +function parseSelectQuery(query) { try { - // Trim the query to remove any leading/trailing whitespaces - query = query.trim (); - - // Initialize distinct flag - let isDistinct = false; - - // Check for DISTINCT keyword and update the query - if (query.toUpperCase ().includes ('SELECT DISTINCT')) { - isDistinct = true; - query = query.replace ('SELECT DISTINCT', 'SELECT'); - } - - // Updated regex to capture LIMIT clause and remove it for further processing - const limitRegex = /\sLIMIT\s(\d+)/i; - const limitMatch = query.match (limitRegex); - - let limit = null; - if (limitMatch) { - limit = parseInt (limitMatch[1], 10); - query = query.replace (limitRegex, ''); // Remove LIMIT clause - } - - // Process ORDER BY clause and remove it for further processing - const orderByRegex = /\sORDER BY\s(.+)/i; - const orderByMatch = query.match (orderByRegex); - let orderByFields = null; - if (orderByMatch) { - orderByFields = orderByMatch[1].split (',').map (field => { - const [fieldName, order] = field.trim ().split (/\s+/); - return {fieldName, order: order ? order.toUpperCase () : 'ASC'}; - }); - query = query.replace (orderByRegex, ''); - } - - // Process GROUP BY clause and remove it for further processing - const groupByRegex = /\sGROUP BY\s(.+)/i; - const groupByMatch = query.match (groupByRegex); - let groupByFields = null; - if (groupByMatch) { - groupByFields = groupByMatch[1].split (',').map (field => field.trim ()); - query = query.replace (groupByRegex, ''); - } - - // Process WHERE clause - const whereSplit = query.split (/\sWHERE\s/i); - const queryWithoutWhere = whereSplit[0]; // Everything before WHERE clause - const whereClause = whereSplit.length > 1 ? whereSplit[1].trim () : null; - - // Process JOIN clause - const joinSplit = queryWithoutWhere.split (/\s(INNER|LEFT|RIGHT) JOIN\s/i); - const selectPart = joinSplit[0].trim (); // Everything before JOIN clause - - // Extract JOIN information - const {joinType, joinTable, joinCondition} = parseJoinClause ( - queryWithoutWhere - ); - - // Parse SELECT part - const selectRegex = /^SELECT\s(.+?)\sFROM\s(.+)/i; - const selectMatch = selectPart.match (selectRegex); - if (!selectMatch) { - throw new Error ('Invalid SELECT format'); - } - const [, fields, table] = selectMatch; - - // Parse WHERE part if it exists - let whereClauses = []; - if (whereClause) { - whereClauses = parseWhereClause (whereClause); - } - - // Check for aggregate functions without GROUP BY - const hasAggregateWithoutGroupBy = checkAggregateWithoutGroupBy ( - query, - groupByFields - ); - - return { - fields: fields.split (',').map (field => field.trim ()), - table: table.trim (), - whereClauses, - joinType, - joinTable, - joinCondition, - groupByFields, - orderByFields, - hasAggregateWithoutGroupBy, - limit, - isDistinct, - }; + + // Trim the query to remove any leading/trailing whitespaces + query = query.trim(); + + // Initialize distinct flag + let isDistinct = false; + + // Check for DISTINCT keyword and update the query + if (query.toUpperCase().includes('SELECT DISTINCT')) { + isDistinct = true; + query = query.replace('SELECT DISTINCT', 'SELECT'); + } + + // Updated regex to capture LIMIT clause and remove it for further processing + const limitRegex = /\sLIMIT\s(\d+)/i; + const limitMatch = query.match(limitRegex); + + let limit = null; + if (limitMatch) { + limit = parseInt(limitMatch[1], 10); + query = query.replace(limitRegex, ''); // Remove LIMIT clause + } + + // Process ORDER BY clause and remove it for further processing + const orderByRegex = /\sORDER BY\s(.+)/i; + const orderByMatch = query.match(orderByRegex); + let orderByFields = null; + if (orderByMatch) { + orderByFields = orderByMatch[1].split(',').map(field => { + const [fieldName, order] = field.trim().split(/\s+/); + return { fieldName, order: order ? order.toUpperCase() : 'ASC' }; + }); + query = query.replace(orderByRegex, ''); + } + + // Process GROUP BY clause and remove it for further processing + const groupByRegex = /\sGROUP BY\s(.+)/i; + const groupByMatch = query.match(groupByRegex); + let groupByFields = null; + if (groupByMatch) { + groupByFields = groupByMatch[1].split(',').map(field => field.trim()); + query = query.replace(groupByRegex, ''); + } + + // Process WHERE clause + const whereSplit = query.split(/\sWHERE\s/i); + const queryWithoutWhere = whereSplit[0]; // Everything before WHERE clause + const whereClause = whereSplit.length > 1 ? whereSplit[1].trim() : null; + + // Process JOIN clause + const joinSplit = queryWithoutWhere.split(/\s(INNER|LEFT|RIGHT) JOIN\s/i); + const selectPart = joinSplit[0].trim(); // Everything before JOIN clause + + // Extract JOIN information + const { joinType, joinTable, joinCondition } = parseJoinClause(queryWithoutWhere); + + // Parse SELECT part + const selectRegex = /^SELECT\s(.+?)\sFROM\s(.+)/i; + const selectMatch = selectPart.match(selectRegex); + if (!selectMatch) { + throw new Error('Invalid SELECT format'); + } + const [, fields, table] = selectMatch; + + // Parse WHERE part if it exists + let whereClauses = []; + if (whereClause) { + whereClauses = parseWhereClause(whereClause); + } + + // Check for aggregate functions without GROUP BY + const hasAggregateWithoutGroupBy = checkAggregateWithoutGroupBy(query, groupByFields); + + return { + fields: fields.split(',').map(field => field.trim()), + table: table.trim(), + whereClauses, + joinType, + joinTable, + joinCondition, + groupByFields, + orderByFields, + hasAggregateWithoutGroupBy, + limit, + isDistinct + }; } catch (error) { - throw new Error (`Query parsing error: ${error.message}`); + throw new Error(`Query parsing error: ${error.message}`); } - } - - function checkAggregateWithoutGroupBy (query, groupByFields) { +} + +function checkAggregateWithoutGroupBy(query, groupByFields) { const aggregateFunctionRegex = /(\bCOUNT\b|\bAVG\b|\bSUM\b|\bMIN\b|\bMAX\b)\s*\(\s*(\*|\w+)\s*\)/i; - return aggregateFunctionRegex.test (query) && !groupByFields; - } - - function parseWhereClause (whereString) { + return aggregateFunctionRegex.test(query) && !groupByFields; +} + +function parseWhereClause(whereString) { const conditionRegex = /(.*?)(=|!=|>=|<=|>|<)(.*)/; - return whereString.split (/ AND | OR /i).map (conditionString => { - if (conditionString.includes (' LIKE ')) { - const [field, pattern] = conditionString.split (/\sLIKE\s/i); - return { - field: field.trim (), - operator: 'LIKE', - value: pattern.trim ().replace (/^'(.*)'$/, '$1'), - }; - } else { - const match = conditionString.match (conditionRegex); - if (match) { - const [, field, operator, value] = match; - return {field: field.trim (), operator, value: value.trim ()}; + return whereString.split(/ AND | OR /i).map(conditionString => { + if (conditionString.includes(' LIKE ')) { + const [field, pattern] = conditionString.split(/\sLIKE\s/i); + return { field: field.trim(), operator: 'LIKE', value: pattern.trim().replace(/^'(.*)'$/, '$1') }; + } else { + const match = conditionString.match(conditionRegex); + if (match) { + const [, field, operator, value] = match; + return { field: field.trim(), operator, value: value.trim() }; + } + throw new Error('Invalid WHERE clause format'); } - throw new Error ('Invalid WHERE clause format'); - } }); - } - - function parseJoinClause (query) { +} + +function parseJoinClause(query) { const joinRegex = /\s(INNER|LEFT|RIGHT) JOIN\s(.+?)\sON\s([\w.]+)\s*=\s*([\w.]+)/i; - const joinMatch = query.match (joinRegex); - + const joinMatch = query.match(joinRegex); + if (joinMatch) { - return { - joinType: joinMatch[1].trim (), - joinTable: joinMatch[2].trim (), - joinCondition: { - left: joinMatch[3].trim (), - right: joinMatch[4].trim (), - }, - }; + return { + joinType: joinMatch[1].trim(), + joinTable: joinMatch[2].trim(), + joinCondition: { + left: joinMatch[3].trim(), + right: joinMatch[4].trim() + } + }; } - + return { - joinType: null, - joinTable: null, - joinCondition: null, + joinType: null, + joinTable: null, + joinCondition: null }; - } - - function parseInsertQuery (query) { +} + +function parseInsertQuery(query) { const insertRegex = /INSERT INTO (\w+)\s\((.+)\)\sVALUES\s\((.+)\)/i; - const match = query.match (insertRegex); - + const match = query.match(insertRegex); + if (!match) { - throw new Error ('Invalid INSERT INTO syntax.'); + throw new Error("Invalid INSERT INTO syntax."); } - + const [, table, columns, values] = match; return { - type: 'INSERT', - table: table.trim (), - columns: columns.split (',').map (column => column.trim ()), - values: values.split (',').map (value => value.trim ()), + type: 'INSERT', + table: table.trim(), + columns: columns.split(',').map(column => column.trim()), + values: values.split(',').map(value => value.trim()) }; - } - - function parseDeleteQuery (query) { +} + +function parseDeleteQuery(query) { const deleteRegex = /DELETE FROM (\w+)( WHERE (.*))?/i; - const match = query.match (deleteRegex); - + const match = query.match(deleteRegex); + if (!match) { - throw new Error ('Invalid DELETE syntax.'); + throw new Error("Invalid DELETE syntax."); } - + const [, table, , whereString] = match; let whereClauses = []; if (whereString) { - whereClauses = parseWhereClause (whereString); + whereClauses = parseWhereClause(whereString); } - + return { - type: 'DELETE', - table: table.trim (), - whereClauses, + type: 'DELETE', + table: table.trim(), + whereClauses }; - } - - module.exports = { - parseSelectQuery, - parseJoinClause, - parseInsertQuery, - parseDeleteQuery, - }; \ No newline at end of file +} + + +module.exports = { parseSelectQuery, parseJoinClause, parseInsertQuery, parseDeleteQuery }; \ No newline at end of file diff --git a/tests/step-02/index.test.js b/tests/step-02/index.test.js index a5467ee48..59a3322e8 100644 --- a/tests/step-02/index.test.js +++ b/tests/step-02/index.test.js @@ -1,9 +1,9 @@ -const readCSV = require('../../src/csvReader'); +const {readCSV} = require('../../src/csvReader'); test('Read CSV File', async () => { - const data = await readCSV('./sample.csv'); + const data = await readCSV('./student.csv'); expect(data.length).toBeGreaterThan(0); - expect(data.length).toBe(3); + expect(data.length).toBe(4); expect(data[0].name).toBe('John'); expect(data[0].age).toBe('30'); //ignore the string type here, we will fix this later }); \ No newline at end of file diff --git a/tests/step-03/index.test.js b/tests/step-03/index.test.js index 9145ad3e4..fcb733a0b 100644 --- a/tests/step-03/index.test.js +++ b/tests/step-03/index.test.js @@ -1,19 +1,29 @@ -const readCSV = require('../../src/csvReader'); -const parseQuery = require('../../src/queryParser'); + +const {readCSV} = require('../../src/csvReader'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { - const data = await readCSV('./sample.csv'); + const data = await readCSV('./student.csv'); expect(data.length).toBeGreaterThan(0); - expect(data.length).toBe(3); + expect(data.length).toBe(4); expect(data[0].name).toBe('John'); expect(data[0].age).toBe('30'); //ignore the string type here, we will fix this later }); test('Parse SQL Query', () => { - const query = 'SELECT id, name FROM sample'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample' + table: 'student', + whereClauses: [], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); \ No newline at end of file diff --git a/tests/step-04/index.test.js b/tests/step-04/index.test.js index bc353dd3d..51e8cfd7d 100644 --- a/tests/step-04/index.test.js +++ b/tests/step-04/index.test.js @@ -1,26 +1,35 @@ -const readCSV = require('../../src/csvReader'); -const parseQuery = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { - const data = await readCSV('./sample.csv'); + const data = await readCSV('./student.csv'); expect(data.length).toBeGreaterThan(0); - expect(data.length).toBe(3); + expect(data.length).toBe(4); expect(data[0].name).toBe('John'); expect(data[0].age).toBe('30'); //ignore the string type here, we will fix this later }); test('Parse SQL Query', () => { - const query = 'SELECT id, name FROM sample'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample' + table: 'student', + whereClauses: [], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Execute SQL Query', async () => { - const query = 'SELECT id, name FROM sample'; + const query = 'SELECT id, name FROM student'; const result = await executeSELECTQuery(query); expect(result.length).toBeGreaterThan(0); expect(result[0]).toHaveProperty('id'); diff --git a/tests/step-05/index.test.js b/tests/step-05/index.test.js index 66a77c061..8522a0db7 100644 --- a/tests/step-05/index.test.js +++ b/tests/step-05/index.test.js @@ -1,27 +1,35 @@ -const readCSV = require('../../src/csvReader'); -const parseQuery = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { - const data = await readCSV('./sample.csv'); + const data = await readCSV('./student.csv'); expect(data.length).toBeGreaterThan(0); - expect(data.length).toBe(3); + expect(data.length).toBe(4); expect(data[0].name).toBe('John'); expect(data[0].age).toBe('30'); //ignore the string type here, we will fix this later }); test('Parse SQL Query', () => { - const query = 'SELECT id, name FROM sample'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample', - whereClause: null + table: 'student', + whereClauses: [], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Execute SQL Query', async () => { - const query = 'SELECT id, name FROM sample'; + const query = 'SELECT id, name FROM student'; const result = await executeSELECTQuery(query); expect(result.length).toBeGreaterThan(0); expect(result[0]).toHaveProperty('id'); @@ -29,19 +37,29 @@ test('Execute SQL Query', async () => { expect(result[0]).not.toHaveProperty('age'); expect(result[0]).toEqual({ id: '1', name: 'John' }); }); - test('Parse SQL Query with WHERE Clause', () => { - const query = 'SELECT id, name FROM sample WHERE age = 25'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student WHERE age = 25'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample', - whereClause: 'age = 25' + table: 'student', + whereClauses: [{ + "field": "age", + "operator": "=", + "value": "25", + }], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); - test('Execute SQL Query with WHERE Clause', async () => { - const query = 'SELECT id, name FROM sample WHERE age = 25'; + const query = 'SELECT id, name FROM student WHERE age = 25'; const result = await executeSELECTQuery(query); expect(result.length).toBe(1); expect(result[0]).toHaveProperty('id'); diff --git a/tests/step-06/index.test.js b/tests/step-06/index.test.js index 2e2ef6416..fe79fabba 100644 --- a/tests/step-06/index.test.js +++ b/tests/step-06/index.test.js @@ -1,27 +1,35 @@ -const readCSV = require('../../src/csvReader'); -const parseQuery = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { - const data = await readCSV('./sample.csv'); + const data = await readCSV('./student.csv'); expect(data.length).toBeGreaterThan(0); - expect(data.length).toBe(3); + expect(data.length).toBe(4); expect(data[0].name).toBe('John'); expect(data[0].age).toBe('30'); //ignore the string type here, we will fix this later }); test('Parse SQL Query', () => { - const query = 'SELECT id, name FROM sample'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample', - whereClauses: [] + table: 'student', + whereClauses: [], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Execute SQL Query', async () => { - const query = 'SELECT id, name FROM sample'; + const query = 'SELECT id, name FROM student'; const result = await executeSELECTQuery(query); expect(result.length).toBeGreaterThan(0); expect(result[0]).toHaveProperty('id'); @@ -29,23 +37,29 @@ test('Execute SQL Query', async () => { expect(result[0]).not.toHaveProperty('age'); expect(result[0]).toEqual({ id: '1', name: 'John' }); }); - test('Parse SQL Query with WHERE Clause', () => { - const query = 'SELECT id, name FROM sample WHERE age = 25'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student WHERE age = 25'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample', + table: 'student', whereClauses: [{ - field: "age", - operator: "=", - value: "25", + "field": "age", + "operator": "=", + "value": "25", }], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); - test('Execute SQL Query with WHERE Clause', async () => { - const query = 'SELECT id, name FROM sample WHERE age = 25'; + const query = 'SELECT id, name FROM student WHERE age = 25'; const result = await executeSELECTQuery(query); expect(result.length).toBe(1); expect(result[0]).toHaveProperty('id'); @@ -54,11 +68,11 @@ test('Execute SQL Query with WHERE Clause', async () => { }); test('Parse SQL Query with Multiple WHERE Clauses', () => { - const query = 'SELECT id, name FROM sample WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample', + table: 'student', whereClauses: [{ "field": "age", "operator": "=", @@ -67,12 +81,20 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { "field": "name", "operator": "=", "value": "John", - }] + }], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); -test('Execute SQL Query with Multiple WHERE Clause', async () => { - const query = 'SELECT id, name FROM sample WHERE age = 30 AND name = John'; +test('Execute SQL Query with Complex WHERE Clause', async () => { + const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; const result = await executeSELECTQuery(query); expect(result.length).toBe(1); expect(result[0]).toEqual({ id: '1', name: 'John' }); diff --git a/tests/step-07/index.test.js b/tests/step-07/index.test.js index ee0ebed5e..5faa49133 100644 --- a/tests/step-07/index.test.js +++ b/tests/step-07/index.test.js @@ -1,27 +1,35 @@ -const readCSV = require('../../src/csvReader'); -const parseQuery = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { - const data = await readCSV('./sample.csv'); + const data = await readCSV('./student.csv'); expect(data.length).toBeGreaterThan(0); - expect(data.length).toBe(3); + expect(data.length).toBe(4); expect(data[0].name).toBe('John'); expect(data[0].age).toBe('30'); //ignore the string type here, we will fix this later }); test('Parse SQL Query', () => { - const query = 'SELECT id, name FROM sample'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample', - whereClauses: [] + table: 'student', + whereClauses: [], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Execute SQL Query', async () => { - const query = 'SELECT id, name FROM sample'; + const query = 'SELECT id, name FROM student'; const result = await executeSELECTQuery(query); expect(result.length).toBeGreaterThan(0); expect(result[0]).toHaveProperty('id'); @@ -29,23 +37,29 @@ test('Execute SQL Query', async () => { expect(result[0]).not.toHaveProperty('age'); expect(result[0]).toEqual({ id: '1', name: 'John' }); }); - test('Parse SQL Query with WHERE Clause', () => { - const query = 'SELECT id, name FROM sample WHERE age = 25'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student WHERE age = 25'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample', + table: 'student', whereClauses: [{ - field: "age", - operator: "=", - value: "25", + "field": "age", + "operator": "=", + "value": "25", }], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); - test('Execute SQL Query with WHERE Clause', async () => { - const query = 'SELECT id, name FROM sample WHERE age = 25'; + const query = 'SELECT id, name FROM student WHERE age = 25'; const result = await executeSELECTQuery(query); expect(result.length).toBe(1); expect(result[0]).toHaveProperty('id'); @@ -54,11 +68,11 @@ test('Execute SQL Query with WHERE Clause', async () => { }); test('Parse SQL Query with Multiple WHERE Clauses', () => { - const query = 'SELECT id, name FROM sample WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], - table: 'sample', + table: 'student', whereClauses: [{ "field": "age", "operator": "=", @@ -67,27 +81,34 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { "field": "name", "operator": "=", "value": "John", - }] + }], + joinCondition: null, + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); -test('Execute SQL Query with Multiple WHERE Clause', async () => { - const query = 'SELECT id, name FROM sample WHERE age = 30 AND name = John'; +test('Execute SQL Query with Complex WHERE Clause', async () => { + const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; const result = await executeSELECTQuery(query); expect(result.length).toBe(1); expect(result[0]).toEqual({ id: '1', name: 'John' }); }); - test('Execute SQL Query with Greater Than', async () => { - const queryWithGT = 'SELECT id FROM sample WHERE age > 22'; + const queryWithGT = 'SELECT id FROM student WHERE age > 22'; const result = await executeSELECTQuery(queryWithGT); - expect(result.length).toEqual(2); + expect(result.length).toEqual(3); expect(result[0]).toHaveProperty('id'); }); test('Execute SQL Query with Not Equal to', async () => { - const queryWithGT = 'SELECT name FROM sample WHERE age != 25'; + const queryWithGT = 'SELECT name FROM student WHERE age != 25'; const result = await executeSELECTQuery(queryWithGT); - expect(result.length).toEqual(2); + expect(result.length).toEqual(3); expect(result[0]).toHaveProperty('name'); }); \ No newline at end of file diff --git a/tests/step-08/index.test.js b/tests/step-08/index.test.js index aab1467e6..93e1799b0 100644 --- a/tests/step-08/index.test.js +++ b/tests/step-08/index.test.js @@ -1,24 +1,30 @@ -const readCSV = require('../../src/csvReader'); -const parseQuery = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); expect(data.length).toBeGreaterThan(0); - expect(data.length).toBe(3); + expect(data.length).toBe(4); expect(data[0].name).toBe('John'); expect(data[0].age).toBe('30'); //ignore the string type here, we will fix this later }); test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', whereClauses: [], joinCondition: null, - joinTable: null + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); @@ -31,10 +37,9 @@ test('Execute SQL Query', async () => { expect(result[0]).not.toHaveProperty('age'); expect(result[0]).toEqual({ id: '1', name: 'John' }); }); - test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -44,10 +49,15 @@ test('Parse SQL Query with WHERE Clause', () => { "value": "25", }], joinCondition: null, - joinTable: null + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); - test('Execute SQL Query with WHERE Clause', async () => { const query = 'SELECT id, name FROM student WHERE age = 25'; const result = await executeSELECTQuery(query); @@ -59,7 +69,7 @@ test('Execute SQL Query with WHERE Clause', async () => { test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -73,7 +83,13 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { "value": "John", }], joinCondition: null, - joinTable: null + joinTable: null, + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); @@ -83,42 +99,53 @@ test('Execute SQL Query with Complex WHERE Clause', async () => { expect(result.length).toBe(1); expect(result[0]).toEqual({ id: '1', name: 'John' }); }); - test('Execute SQL Query with Greater Than', async () => { const queryWithGT = 'SELECT id FROM student WHERE age > 22'; const result = await executeSELECTQuery(queryWithGT); - expect(result.length).toEqual(2); + expect(result.length).toEqual(3); expect(result[0]).toHaveProperty('id'); }); test('Execute SQL Query with Not Equal to', async () => { const queryWithGT = 'SELECT name FROM student WHERE age != 25'; const result = await executeSELECTQuery(queryWithGT); - expect(result.length).toEqual(2); + expect(result.length).toEqual(3); expect(result[0]).toHaveProperty('name'); }); test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', whereClauses: [], joinTable: 'enrollment', - joinCondition: { left: 'student.id', right: 'enrollment.student_id' } + joinType: "INNER", + joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }) }); test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', whereClauses: [{ field: 'student.age', operator: '>', value: '20' }], joinTable: 'enrollment', - joinCondition: { left: 'student.id', right: 'enrollment.student_id' } + joinType: "INNER", + joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }) }); diff --git a/tests/step-09/index.test.js b/tests/step-09/index.test.js index aaf711f5a..02e6cdb86 100644 --- a/tests/step-09/index.test.js +++ b/tests/step-09/index.test.js @@ -1,6 +1,6 @@ -const readCSV = require('../../src/csvReader'); -const {parseQuery} = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); @@ -12,14 +12,19 @@ test('Read CSV File', async () => { test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', whereClauses: [], joinCondition: null, joinTable: null, - joinType: null + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); @@ -32,10 +37,9 @@ test('Execute SQL Query', async () => { expect(result[0]).not.toHaveProperty('age'); expect(result[0]).toEqual({ id: '1', name: 'John' }); }); - test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -46,10 +50,14 @@ test('Parse SQL Query with WHERE Clause', () => { }], joinCondition: null, joinTable: null, - joinType: null + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); - test('Execute SQL Query with WHERE Clause', async () => { const query = 'SELECT id, name FROM student WHERE age = 25'; const result = await executeSELECTQuery(query); @@ -61,7 +69,7 @@ test('Execute SQL Query with WHERE Clause', async () => { test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -76,7 +84,12 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { }], joinCondition: null, joinTable: null, - joinType: null + joinType: null, + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); @@ -86,7 +99,6 @@ test('Execute SQL Query with Complex WHERE Clause', async () => { expect(result.length).toBe(1); expect(result[0]).toEqual({ id: '1', name: 'John' }); }); - test('Execute SQL Query with Greater Than', async () => { const queryWithGT = 'SELECT id FROM student WHERE age > 22'; const result = await executeSELECTQuery(queryWithGT); @@ -103,27 +115,37 @@ test('Execute SQL Query with Not Equal to', async () => { test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', whereClauses: [], joinTable: 'enrollment', + joinType: "INNER", joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, - joinType: 'INNER' + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }) }); test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', whereClauses: [{ field: 'student.age', operator: '>', value: '20' }], joinTable: 'enrollment', + joinType: "INNER", joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, - joinType: 'INNER' + groupByFields: null, + hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }) }); diff --git a/tests/step-10/index.test.js b/tests/step-10/index.test.js index 5e118eda5..2b9ef2e73 100644 --- a/tests/step-10/index.test.js +++ b/tests/step-10/index.test.js @@ -1,6 +1,6 @@ -const readCSV = require('../../src/csvReader'); -const {parseQuery, parseJoinClause} = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); @@ -258,7 +258,7 @@ test('Average age of students above a certain age', async () => { test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -268,12 +268,15 @@ test('Parse SQL Query', () => { joinType: null, groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -287,12 +290,15 @@ test('Parse SQL Query with WHERE Clause', () => { joinType: null, groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -310,12 +316,15 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { joinType: null, groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -325,12 +334,15 @@ test('Parse SQL Query with INNER JOIN', async () => { joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }) }); test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -340,6 +352,9 @@ test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }) }); @@ -387,7 +402,7 @@ test('Returns null for queries without JOIN', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -397,12 +412,15 @@ test('Parse LEFT Join Query Completely', () => { joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }) }) test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -412,12 +430,15 @@ test('Parse LEFT Join Query Completely', () => { joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }) }) test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age > 22'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -427,12 +448,15 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main tabl "whereClauses": [{ "field": "student.age", "operator": ">", "value": "22" }], groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Physics'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -442,12 +466,15 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join tabl "whereClauses": [{ "field": "enrollment.course", "operator": "=", "value": "'Physics'" }], groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age < 25'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -457,12 +484,15 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main tab "whereClauses": [{ "field": "student.age", "operator": "<", "value": "25" }], groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Chemistry'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -472,13 +502,16 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join tab "whereClauses": [{ "field": "enrollment.course", "operator": "=", "value": "'Chemistry'" }], groupByFields: null, hasAggregateWithoutGroupBy: false, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse COUNT Aggregate Query', () => { const query = 'SELECT COUNT(*) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['COUNT(*)'], table: 'student', @@ -488,13 +521,16 @@ test('Parse COUNT Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SUM Aggregate Query', () => { const query = 'SELECT SUM(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['SUM(age)'], table: 'student', @@ -504,12 +540,15 @@ test('Parse SUM Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse AVG Aggregate Query', () => { const query = 'SELECT AVG(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['AVG(age)'], table: 'student', @@ -519,12 +558,15 @@ test('Parse AVG Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse MIN Aggregate Query', () => { const query = 'SELECT MIN(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MIN(age)'], table: 'student', @@ -534,12 +576,15 @@ test('Parse MIN Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse MAX Aggregate Query', () => { const query = 'SELECT MAX(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MAX(age)'], table: 'student', @@ -549,12 +594,15 @@ test('Parse MAX Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse basic GROUP BY query', () => { const query = 'SELECT age, COUNT(*) FROM student GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -563,13 +611,16 @@ test('Parse basic GROUP BY query', () => { joinType: null, joinTable: null, joinCondition: null, - hasAggregateWithoutGroupBy: false + hasAggregateWithoutGroupBy: false, + orderByFields: null, + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with WHERE clause', () => { const query = 'SELECT age, COUNT(*) FROM student WHERE age > 22 GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -578,13 +629,16 @@ test('Parse GROUP BY query with WHERE clause', () => { joinType: null, joinTable: null, joinCondition: null, - hasAggregateWithoutGroupBy: false + hasAggregateWithoutGroupBy: false, + orderByFields: null, + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with multiple fields', () => { const query = 'SELECT student_id, course, COUNT(*) FROM enrollment GROUP BY student_id, course'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student_id', 'course', 'COUNT(*)'], table: 'enrollment', @@ -593,13 +647,16 @@ test('Parse GROUP BY query with multiple fields', () => { joinType: null, joinTable: null, joinCondition: null, - hasAggregateWithoutGroupBy: false + hasAggregateWithoutGroupBy: false, + orderByFields: null, + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with JOIN and WHERE clauses', () => { const query = 'SELECT student.name, COUNT(*) FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE enrollment.course = "Mathematics" GROUP BY student.name'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student.name', 'COUNT(*)'], table: 'student', @@ -611,6 +668,9 @@ test('Parse GROUP BY query with JOIN and WHERE clauses', () => { left: 'student.id', right: 'enrollment.student_id' }, - hasAggregateWithoutGroupBy: false + hasAggregateWithoutGroupBy: false, + orderByFields: null, + "limit": null, + isDistinct: false, }); }); \ No newline at end of file diff --git a/tests/step-11/index.test.js b/tests/step-11/index.test.js index 1cf5f2def..68f672e72 100644 --- a/tests/step-11/index.test.js +++ b/tests/step-11/index.test.js @@ -1,6 +1,6 @@ -const readCSV = require('../../src/csvReader'); -const {parseQuery, parseJoinClause} = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); @@ -258,7 +258,7 @@ test('Average age of students above a certain age', async () => { test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -268,13 +268,15 @@ test('Parse SQL Query', () => { joinType: null, groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -288,13 +290,15 @@ test('Parse SQL Query with WHERE Clause', () => { joinType: null, groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -312,13 +316,15 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { joinType: null, groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -328,13 +334,15 @@ test('Parse SQL Query with INNER JOIN', async () => { joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }) }); test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -344,7 +352,9 @@ test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }) }); @@ -392,7 +402,7 @@ test('Returns null for queries without JOIN', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -402,13 +412,15 @@ test('Parse LEFT Join Query Completely', () => { joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }) }) test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -418,13 +430,15 @@ test('Parse LEFT Join Query Completely', () => { joinCondition: { left: 'student.id', right: 'enrollment.student_id' }, groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }) }) test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age > 22'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -434,13 +448,15 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main tabl "whereClauses": [{ "field": "student.age", "operator": ">", "value": "22" }], groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Physics'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -450,13 +466,15 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join tabl "whereClauses": [{ "field": "enrollment.course", "operator": "=", "value": "'Physics'" }], groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age < 25'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -466,13 +484,15 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main tab "whereClauses": [{ "field": "student.age", "operator": "<", "value": "25" }], groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Chemistry'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -482,14 +502,16 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join tab "whereClauses": [{ "field": "enrollment.course", "operator": "=", "value": "'Chemistry'" }], groupByFields: null, hasAggregateWithoutGroupBy: false, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse COUNT Aggregate Query', () => { const query = 'SELECT COUNT(*) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['COUNT(*)'], table: 'student', @@ -499,14 +521,16 @@ test('Parse COUNT Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse SUM Aggregate Query', () => { const query = 'SELECT SUM(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['SUM(age)'], table: 'student', @@ -516,13 +540,15 @@ test('Parse SUM Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse AVG Aggregate Query', () => { const query = 'SELECT AVG(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['AVG(age)'], table: 'student', @@ -532,13 +558,15 @@ test('Parse AVG Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse MIN Aggregate Query', () => { const query = 'SELECT MIN(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MIN(age)'], table: 'student', @@ -548,13 +576,15 @@ test('Parse MIN Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse MAX Aggregate Query', () => { const query = 'SELECT MAX(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MAX(age)'], table: 'student', @@ -564,13 +594,15 @@ test('Parse MAX Aggregate Query', () => { "joinCondition": null, "joinTable": null, "joinType": null, - "orderByFields": null + "orderByFields": null, + "limit": null, + isDistinct: false }); }); test('Parse basic GROUP BY query', () => { const query = 'SELECT age, COUNT(*) FROM student GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -580,13 +612,15 @@ test('Parse basic GROUP BY query', () => { joinTable: null, joinCondition: null, hasAggregateWithoutGroupBy: false, - orderByFields: null + orderByFields: null, + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with WHERE clause', () => { const query = 'SELECT age, COUNT(*) FROM student WHERE age > 22 GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -596,13 +630,15 @@ test('Parse GROUP BY query with WHERE clause', () => { joinTable: null, joinCondition: null, hasAggregateWithoutGroupBy: false, - orderByFields: null + orderByFields: null, + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with multiple fields', () => { const query = 'SELECT student_id, course, COUNT(*) FROM enrollment GROUP BY student_id, course'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student_id', 'course', 'COUNT(*)'], table: 'enrollment', @@ -612,13 +648,15 @@ test('Parse GROUP BY query with multiple fields', () => { joinTable: null, joinCondition: null, hasAggregateWithoutGroupBy: false, - orderByFields: null + orderByFields: null, + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with JOIN and WHERE clauses', () => { const query = 'SELECT student.name, COUNT(*) FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE enrollment.course = "Mathematics" GROUP BY student.name'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student.name', 'COUNT(*)'], table: 'student', @@ -631,7 +669,9 @@ test('Parse GROUP BY query with JOIN and WHERE clauses', () => { right: 'enrollment.student_id' }, hasAggregateWithoutGroupBy: false, - orderByFields: null + orderByFields: null, + "limit": null, + isDistinct: false, }); }); diff --git a/tests/step-12/index.test.js b/tests/step-12/index.test.js index d15c77ef5..d6a7a6913 100644 --- a/tests/step-12/index.test.js +++ b/tests/step-12/index.test.js @@ -1,6 +1,6 @@ -const readCSV = require('../../src/csvReader'); -const {parseQuery, parseJoinClause} = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); @@ -258,7 +258,7 @@ test('Average age of students above a certain age', async () => { test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -269,13 +269,14 @@ test('Parse SQL Query', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -290,13 +291,14 @@ test('Parse SQL Query with WHERE Clause', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -315,13 +317,14 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -332,13 +335,14 @@ test('Parse SQL Query with INNER JOIN', async () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }) }); test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -349,7 +353,8 @@ test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }) }); @@ -397,7 +402,7 @@ test('Returns null for queries without JOIN', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -408,13 +413,14 @@ test('Parse LEFT Join Query Completely', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }) }) test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -425,13 +431,14 @@ test('Parse LEFT Join Query Completely', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }) }) test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age > 22'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -442,13 +449,14 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main tabl groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Physics'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -459,13 +467,14 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join tabl groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age < 25'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -476,13 +485,14 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main tab groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Chemistry'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -493,14 +503,15 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join tab groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse COUNT Aggregate Query', () => { const query = 'SELECT COUNT(*) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['COUNT(*)'], table: 'student', @@ -511,14 +522,15 @@ test('Parse COUNT Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SUM Aggregate Query', () => { const query = 'SELECT SUM(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['SUM(age)'], table: 'student', @@ -529,13 +541,14 @@ test('Parse SUM Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse AVG Aggregate Query', () => { const query = 'SELECT AVG(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['AVG(age)'], table: 'student', @@ -546,13 +559,14 @@ test('Parse AVG Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse MIN Aggregate Query', () => { const query = 'SELECT MIN(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MIN(age)'], table: 'student', @@ -563,13 +577,14 @@ test('Parse MIN Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse MAX Aggregate Query', () => { const query = 'SELECT MAX(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MAX(age)'], table: 'student', @@ -580,13 +595,14 @@ test('Parse MAX Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse basic GROUP BY query', () => { const query = 'SELECT age, COUNT(*) FROM student GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -597,13 +613,14 @@ test('Parse basic GROUP BY query', () => { joinCondition: null, hasAggregateWithoutGroupBy: false, orderByFields: null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with WHERE clause', () => { const query = 'SELECT age, COUNT(*) FROM student WHERE age > 22 GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -614,13 +631,14 @@ test('Parse GROUP BY query with WHERE clause', () => { joinCondition: null, hasAggregateWithoutGroupBy: false, orderByFields: null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with multiple fields', () => { const query = 'SELECT student_id, course, COUNT(*) FROM enrollment GROUP BY student_id, course'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student_id', 'course', 'COUNT(*)'], table: 'enrollment', @@ -631,13 +649,14 @@ test('Parse GROUP BY query with multiple fields', () => { joinCondition: null, hasAggregateWithoutGroupBy: false, orderByFields: null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with JOIN and WHERE clauses', () => { const query = 'SELECT student.name, COUNT(*) FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE enrollment.course = "Mathematics" GROUP BY student.name'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student.name', 'COUNT(*)'], table: 'student', @@ -652,6 +671,7 @@ test('Parse GROUP BY query with JOIN and WHERE clauses', () => { hasAggregateWithoutGroupBy: false, orderByFields: null, "limit": null, + isDistinct: false, }); }); diff --git a/tests/step-13/index.test.js b/tests/step-13/index.test.js index 0797faaba..5fe667f4c 100644 --- a/tests/step-13/index.test.js +++ b/tests/step-13/index.test.js @@ -1,6 +1,6 @@ -const readCSV = require('../../src/csvReader'); -const {parseQuery, parseJoinClause} = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); @@ -258,7 +258,7 @@ test('Average age of students above a certain age', async () => { test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -269,13 +269,14 @@ test('Parse SQL Query', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -290,13 +291,14 @@ test('Parse SQL Query with WHERE Clause', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -315,13 +317,14 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -332,13 +335,14 @@ test('Parse SQL Query with INNER JOIN', async () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }) }); test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -349,7 +353,8 @@ test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }) }); @@ -397,7 +402,7 @@ test('Returns null for queries without JOIN', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -408,13 +413,14 @@ test('Parse LEFT Join Query Completely', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }) }) test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -425,13 +431,14 @@ test('Parse LEFT Join Query Completely', () => { groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }) }) test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age > 22'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -442,13 +449,14 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main tabl groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Physics'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -459,13 +467,14 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join tabl groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age < 25'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -476,13 +485,14 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main tab groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Chemistry'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -493,14 +503,15 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join tab groupByFields: null, hasAggregateWithoutGroupBy: false, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse COUNT Aggregate Query', () => { const query = 'SELECT COUNT(*) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['COUNT(*)'], table: 'student', @@ -511,14 +522,15 @@ test('Parse COUNT Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse SUM Aggregate Query', () => { const query = 'SELECT SUM(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['SUM(age)'], table: 'student', @@ -529,13 +541,14 @@ test('Parse SUM Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse AVG Aggregate Query', () => { const query = 'SELECT AVG(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['AVG(age)'], table: 'student', @@ -546,13 +559,14 @@ test('Parse AVG Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse MIN Aggregate Query', () => { const query = 'SELECT MIN(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MIN(age)'], table: 'student', @@ -563,13 +577,14 @@ test('Parse MIN Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse MAX Aggregate Query', () => { const query = 'SELECT MAX(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MAX(age)'], table: 'student', @@ -580,13 +595,14 @@ test('Parse MAX Aggregate Query', () => { "joinTable": null, "joinType": null, "orderByFields": null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse basic GROUP BY query', () => { const query = 'SELECT age, COUNT(*) FROM student GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -597,13 +613,14 @@ test('Parse basic GROUP BY query', () => { joinCondition: null, hasAggregateWithoutGroupBy: false, orderByFields: null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with WHERE clause', () => { const query = 'SELECT age, COUNT(*) FROM student WHERE age > 22 GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -614,13 +631,14 @@ test('Parse GROUP BY query with WHERE clause', () => { joinCondition: null, hasAggregateWithoutGroupBy: false, orderByFields: null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with multiple fields', () => { const query = 'SELECT student_id, course, COUNT(*) FROM enrollment GROUP BY student_id, course'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student_id', 'course', 'COUNT(*)'], table: 'enrollment', @@ -631,13 +649,14 @@ test('Parse GROUP BY query with multiple fields', () => { joinCondition: null, hasAggregateWithoutGroupBy: false, orderByFields: null, - "limit": null + "limit": null, + isDistinct: false }); }); test('Parse GROUP BY query with JOIN and WHERE clauses', () => { const query = 'SELECT student.name, COUNT(*) FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE enrollment.course = "Mathematics" GROUP BY student.name'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student.name', 'COUNT(*)'], table: 'student', @@ -652,6 +671,7 @@ test('Parse GROUP BY query with JOIN and WHERE clauses', () => { hasAggregateWithoutGroupBy: false, orderByFields: null, "limit": null, + isDistinct: false, }); }); diff --git a/tests/step-14/index.test.js b/tests/step-14/index.test.js index 502411fa7..0bc0e0607 100644 --- a/tests/step-14/index.test.js +++ b/tests/step-14/index.test.js @@ -1,6 +1,6 @@ -const readCSV = require('../../src/csvReader'); -const {parseQuery, parseJoinClause} = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); @@ -258,7 +258,7 @@ test('Average age of students above a certain age', async () => { test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -276,7 +276,7 @@ test('Parse SQL Query', () => { test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -298,7 +298,7 @@ test('Parse SQL Query with WHERE Clause', () => { test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -324,7 +324,7 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -342,7 +342,7 @@ test('Parse SQL Query with INNER JOIN', async () => { test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -402,7 +402,7 @@ test('Returns null for queries without JOIN', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -420,7 +420,7 @@ test('Parse LEFT Join Query Completely', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -438,7 +438,7 @@ test('Parse LEFT Join Query Completely', () => { test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age > 22'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -456,7 +456,7 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main tabl test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Physics'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -474,7 +474,7 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join tabl test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age < 25'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -492,7 +492,7 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main tab test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Chemistry'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -511,7 +511,7 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join tab test('Parse COUNT Aggregate Query', () => { const query = 'SELECT COUNT(*) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['COUNT(*)'], table: 'student', @@ -530,7 +530,7 @@ test('Parse COUNT Aggregate Query', () => { test('Parse SUM Aggregate Query', () => { const query = 'SELECT SUM(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['SUM(age)'], table: 'student', @@ -548,7 +548,7 @@ test('Parse SUM Aggregate Query', () => { test('Parse AVG Aggregate Query', () => { const query = 'SELECT AVG(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['AVG(age)'], table: 'student', @@ -566,7 +566,7 @@ test('Parse AVG Aggregate Query', () => { test('Parse MIN Aggregate Query', () => { const query = 'SELECT MIN(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MIN(age)'], table: 'student', @@ -584,7 +584,7 @@ test('Parse MIN Aggregate Query', () => { test('Parse MAX Aggregate Query', () => { const query = 'SELECT MAX(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MAX(age)'], table: 'student', @@ -602,7 +602,7 @@ test('Parse MAX Aggregate Query', () => { test('Parse basic GROUP BY query', () => { const query = 'SELECT age, COUNT(*) FROM student GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -620,7 +620,7 @@ test('Parse basic GROUP BY query', () => { test('Parse GROUP BY query with WHERE clause', () => { const query = 'SELECT age, COUNT(*) FROM student WHERE age > 22 GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -638,7 +638,7 @@ test('Parse GROUP BY query with WHERE clause', () => { test('Parse GROUP BY query with multiple fields', () => { const query = 'SELECT student_id, course, COUNT(*) FROM enrollment GROUP BY student_id, course'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student_id', 'course', 'COUNT(*)'], table: 'enrollment', @@ -656,7 +656,7 @@ test('Parse GROUP BY query with multiple fields', () => { test('Parse GROUP BY query with JOIN and WHERE clauses', () => { const query = 'SELECT student.name, COUNT(*) FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE enrollment.course = "Mathematics" GROUP BY student.name'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student.name', 'COUNT(*)'], table: 'student', diff --git a/tests/step-15/index.test.js b/tests/step-15/index.test.js index a2aa4daee..dc1fa19ae 100644 --- a/tests/step-15/index.test.js +++ b/tests/step-15/index.test.js @@ -1,6 +1,6 @@ -const readCSV = require('../../src/csvReader'); -const {parseQuery, parseJoinClause} = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); @@ -258,7 +258,7 @@ test('Average age of students above a certain age', async () => { test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -276,7 +276,7 @@ test('Parse SQL Query', () => { test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -298,7 +298,7 @@ test('Parse SQL Query with WHERE Clause', () => { test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -324,7 +324,7 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -342,7 +342,7 @@ test('Parse SQL Query with INNER JOIN', async () => { test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -402,7 +402,7 @@ test('Returns null for queries without JOIN', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -420,7 +420,7 @@ test('Parse LEFT Join Query Completely', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -438,7 +438,7 @@ test('Parse LEFT Join Query Completely', () => { test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age > 22'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -456,7 +456,7 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main tabl test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Physics'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -474,7 +474,7 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join tabl test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age < 25'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -492,7 +492,7 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main tab test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Chemistry'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -511,7 +511,7 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join tab test('Parse COUNT Aggregate Query', () => { const query = 'SELECT COUNT(*) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['COUNT(*)'], table: 'student', @@ -530,7 +530,7 @@ test('Parse COUNT Aggregate Query', () => { test('Parse SUM Aggregate Query', () => { const query = 'SELECT SUM(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['SUM(age)'], table: 'student', @@ -548,7 +548,7 @@ test('Parse SUM Aggregate Query', () => { test('Parse AVG Aggregate Query', () => { const query = 'SELECT AVG(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['AVG(age)'], table: 'student', @@ -566,7 +566,7 @@ test('Parse AVG Aggregate Query', () => { test('Parse MIN Aggregate Query', () => { const query = 'SELECT MIN(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MIN(age)'], table: 'student', @@ -584,7 +584,7 @@ test('Parse MIN Aggregate Query', () => { test('Parse MAX Aggregate Query', () => { const query = 'SELECT MAX(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MAX(age)'], table: 'student', @@ -602,7 +602,7 @@ test('Parse MAX Aggregate Query', () => { test('Parse basic GROUP BY query', () => { const query = 'SELECT age, COUNT(*) FROM student GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -620,7 +620,7 @@ test('Parse basic GROUP BY query', () => { test('Parse GROUP BY query with WHERE clause', () => { const query = 'SELECT age, COUNT(*) FROM student WHERE age > 22 GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -638,7 +638,7 @@ test('Parse GROUP BY query with WHERE clause', () => { test('Parse GROUP BY query with multiple fields', () => { const query = 'SELECT student_id, course, COUNT(*) FROM enrollment GROUP BY student_id, course'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student_id', 'course', 'COUNT(*)'], table: 'enrollment', @@ -656,7 +656,7 @@ test('Parse GROUP BY query with multiple fields', () => { test('Parse GROUP BY query with JOIN and WHERE clauses', () => { const query = 'SELECT student.name, COUNT(*) FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE enrollment.course = "Mathematics" GROUP BY student.name'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student.name', 'COUNT(*)'], table: 'student', diff --git a/tests/step-16/index.test.js b/tests/step-16/index.test.js index a2aa4daee..dc1fa19ae 100644 --- a/tests/step-16/index.test.js +++ b/tests/step-16/index.test.js @@ -1,6 +1,6 @@ -const readCSV = require('../../src/csvReader'); -const {parseQuery, parseJoinClause} = require('../../src/queryParser'); -const executeSELECTQuery = require('../../src/index'); +const {readCSV} = require('../../src/csvReader'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); +const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { const data = await readCSV('./student.csv'); @@ -258,7 +258,7 @@ test('Average age of students above a certain age', async () => { test('Parse SQL Query', () => { const query = 'SELECT id, name FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -276,7 +276,7 @@ test('Parse SQL Query', () => { test('Parse SQL Query with WHERE Clause', () => { const query = 'SELECT id, name FROM student WHERE age = 25'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -298,7 +298,7 @@ test('Parse SQL Query with WHERE Clause', () => { test('Parse SQL Query with Multiple WHERE Clauses', () => { const query = 'SELECT id, name FROM student WHERE age = 30 AND name = John'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['id', 'name'], table: 'student', @@ -324,7 +324,7 @@ test('Parse SQL Query with Multiple WHERE Clauses', () => { test('Parse SQL Query with INNER JOIN', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id=enrollment.student_id'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -342,7 +342,7 @@ test('Parse SQL Query with INNER JOIN', async () => { test('Parse SQL Query with INNER JOIN and WHERE Clause', async () => { const query = 'SELECT student.name, enrollment.course FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE student.age > 20'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -402,7 +402,7 @@ test('Returns null for queries without JOIN', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -420,7 +420,7 @@ test('Parse LEFT Join Query Completely', () => { test('Parse LEFT Join Query Completely', () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id'; - const result = parseQuery(query); + const result = parseSelectQuery(query); expect(result).toEqual({ fields: ['student.name', 'enrollment.course'], table: 'student', @@ -438,7 +438,7 @@ test('Parse LEFT Join Query Completely', () => { test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age > 22'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -456,7 +456,7 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the main tabl test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student LEFT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Physics'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -474,7 +474,7 @@ test('Parse SQL Query with LEFT JOIN with a WHERE clause filtering the join tabl test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main table', async () => { const query = 'SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE student.age < 25'; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -492,7 +492,7 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the main tab test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join table', async () => { const query = `SELECT student.name, enrollment.course FROM student RIGHT JOIN enrollment ON student.id=enrollment.student_id WHERE enrollment.course = 'Chemistry'`; - const result = await parseQuery(query); + const result = await parseSelectQuery(query); expect(result).toEqual({ "fields": ["student.name", "enrollment.course"], "joinCondition": { "left": "student.id", "right": "enrollment.student_id" }, @@ -511,7 +511,7 @@ test('Parse SQL Query with RIGHT JOIN with a WHERE clause filtering the join tab test('Parse COUNT Aggregate Query', () => { const query = 'SELECT COUNT(*) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['COUNT(*)'], table: 'student', @@ -530,7 +530,7 @@ test('Parse COUNT Aggregate Query', () => { test('Parse SUM Aggregate Query', () => { const query = 'SELECT SUM(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['SUM(age)'], table: 'student', @@ -548,7 +548,7 @@ test('Parse SUM Aggregate Query', () => { test('Parse AVG Aggregate Query', () => { const query = 'SELECT AVG(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['AVG(age)'], table: 'student', @@ -566,7 +566,7 @@ test('Parse AVG Aggregate Query', () => { test('Parse MIN Aggregate Query', () => { const query = 'SELECT MIN(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MIN(age)'], table: 'student', @@ -584,7 +584,7 @@ test('Parse MIN Aggregate Query', () => { test('Parse MAX Aggregate Query', () => { const query = 'SELECT MAX(age) FROM student'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['MAX(age)'], table: 'student', @@ -602,7 +602,7 @@ test('Parse MAX Aggregate Query', () => { test('Parse basic GROUP BY query', () => { const query = 'SELECT age, COUNT(*) FROM student GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -620,7 +620,7 @@ test('Parse basic GROUP BY query', () => { test('Parse GROUP BY query with WHERE clause', () => { const query = 'SELECT age, COUNT(*) FROM student WHERE age > 22 GROUP BY age'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['age', 'COUNT(*)'], table: 'student', @@ -638,7 +638,7 @@ test('Parse GROUP BY query with WHERE clause', () => { test('Parse GROUP BY query with multiple fields', () => { const query = 'SELECT student_id, course, COUNT(*) FROM enrollment GROUP BY student_id, course'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student_id', 'course', 'COUNT(*)'], table: 'enrollment', @@ -656,7 +656,7 @@ test('Parse GROUP BY query with multiple fields', () => { test('Parse GROUP BY query with JOIN and WHERE clauses', () => { const query = 'SELECT student.name, COUNT(*) FROM student INNER JOIN enrollment ON student.id = enrollment.student_id WHERE enrollment.course = "Mathematics" GROUP BY student.name'; - const parsed = parseQuery(query); + const parsed = parseSelectQuery(query); expect(parsed).toEqual({ fields: ['student.name', 'COUNT(*)'], table: 'student', diff --git a/tests/step-17/index.test.js b/tests/step-17/index.test.js index c99d01fbb..dc1fa19ae 100644 --- a/tests/step-17/index.test.js +++ b/tests/step-17/index.test.js @@ -1,5 +1,5 @@ const {readCSV} = require('../../src/csvReader'); -const {executeSELECTQuery } = require('../../src/index'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { diff --git a/tests/step-17/insertExecuter.test.js b/tests/step-17/insertExecuter.test.js index 8c405f727..9154b0e2e 100644 --- a/tests/step-17/insertExecuter.test.js +++ b/tests/step-17/insertExecuter.test.js @@ -1,4 +1,4 @@ -const { executeINSERTQuery } = require('../../src/index'); +const { executeINSERTQuery } = require('../../src/queryExecutor'); const { readCSV, writeCSV } = require('../../src/csvReader'); const fs = require('fs'); @@ -9,6 +9,7 @@ async function createGradesCSV() { { student_id: '2', course: 'Chemistry', grade: 'B' }, { student_id: '3', course: 'Mathematics', grade: 'C' } ]; + fs.writeFileSync('grades.csv', ''); await writeCSV('grades.csv', initialData); } diff --git a/tests/step-18/deleteExecutor.test.js b/tests/step-18/deleteExecutor.test.js index 11ae617b7..636403858 100644 --- a/tests/step-18/deleteExecutor.test.js +++ b/tests/step-18/deleteExecutor.test.js @@ -1,4 +1,4 @@ -const { executeDELETEQuery } = require('../../src/index'); +const { executeDELETEQuery } = require('../../src/queryExecutor'); const { readCSV, writeCSV } = require('../../src/csvReader'); const fs = require('fs'); diff --git a/tests/step-18/index.test.js b/tests/step-18/index.test.js index c99d01fbb..dc1fa19ae 100644 --- a/tests/step-18/index.test.js +++ b/tests/step-18/index.test.js @@ -1,5 +1,5 @@ const {readCSV} = require('../../src/csvReader'); -const {executeSELECTQuery } = require('../../src/index'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { diff --git a/tests/step-18/insertExecuter.test.js b/tests/step-18/insertExecuter.test.js index 8c405f727..581d17f73 100644 --- a/tests/step-18/insertExecuter.test.js +++ b/tests/step-18/insertExecuter.test.js @@ -1,4 +1,4 @@ -const { executeINSERTQuery } = require('../../src/index'); +const { executeINSERTQuery } = require('../../src/queryExecutor'); const { readCSV, writeCSV } = require('../../src/csvReader'); const fs = require('fs'); diff --git a/tests/step-19/.cph/.insertExecuter.test.js_344b632c9c4863f3843be516d9be4761.prob b/tests/step-19/.cph/.insertExecuter.test.js_344b632c9c4863f3843be516d9be4761.prob new file mode 100644 index 000000000..a677ab136 --- /dev/null +++ b/tests/step-19/.cph/.insertExecuter.test.js_344b632c9c4863f3843be516d9be4761.prob @@ -0,0 +1 @@ +{"name":"Local: insertExecuter","url":"/home/pratyush/github-classroom/status-20X/stylusdb-sql-assignment-bruno-noir/tests/step-19/insertExecuter.test.js","tests":[{"id":1714807548051,"input":"","output":""}],"interactive":false,"memoryLimit":1024,"timeLimit":3000,"srcPath":"/home/pratyush/github-classroom/status-20X/stylusdb-sql-assignment-bruno-noir/tests/step-19/insertExecuter.test.js","group":"local","local":true} \ No newline at end of file diff --git a/tests/step-19/deleteExecutor.test.js b/tests/step-19/deleteExecutor.test.js index 11ae617b7..636403858 100644 --- a/tests/step-19/deleteExecutor.test.js +++ b/tests/step-19/deleteExecutor.test.js @@ -1,4 +1,4 @@ -const { executeDELETEQuery } = require('../../src/index'); +const { executeDELETEQuery } = require('../../src/queryExecutor'); const { readCSV, writeCSV } = require('../../src/csvReader'); const fs = require('fs'); diff --git a/tests/step-19/index.test.js b/tests/step-19/index.test.js index c99d01fbb..dc1fa19ae 100644 --- a/tests/step-19/index.test.js +++ b/tests/step-19/index.test.js @@ -1,5 +1,5 @@ const {readCSV} = require('../../src/csvReader'); -const {executeSELECTQuery } = require('../../src/index'); +const {executeSELECTQuery } = require('../../src/queryExecutor'); const { parseJoinClause, parseSelectQuery } = require('../../src/queryParser'); test('Read CSV File', async () => { diff --git a/tests/step-19/insertExecuter.test.js b/tests/step-19/insertExecuter.test.js index 8c405f727..95453731b 100644 --- a/tests/step-19/insertExecuter.test.js +++ b/tests/step-19/insertExecuter.test.js @@ -1,33 +1,72 @@ -const { executeINSERTQuery } = require('../../src/index'); +const { executeINSERTQuery } = require('../../src/queryExecutor'); const { readCSV, writeCSV } = require('../../src/csvReader'); const fs = require('fs'); -// Helper function to create grades.csv with initial data + async function createGradesCSV() { - const initialData = [ - { student_id: '1', course: 'Mathematics', grade: 'A' }, - { student_id: '2', course: 'Chemistry', grade: 'B' }, - { student_id: '3', course: 'Mathematics', grade: 'C' } - ]; - await writeCSV('grades.csv', initialData); + + const initialData = [ + + { student_id: "1", course: "Mathematics", grade: "A" }, + + { student_id: "2", course: "Chemistry", grade: "B" }, + + { student_id: "3", course: "Mathematics", grade: "C" }, + + ]; + + await writeCSV("grades.csv", initialData); + } + + + // Test to INSERT a new grade and verify -test('Execute INSERT INTO Query for grades.csv', async () => { - // Create grades.csv with initial data - await createGradesCSV(); - - // Execute INSERT statement - const insertQuery = "INSERT INTO grades (student_id, course, grade) VALUES ('4', 'Physics', 'A')"; - await executeINSERTQuery(insertQuery); - - // Verify the new entry - const updatedData = await readCSV('grades.csv'); - const newEntry = updatedData.find(row => row.student_id === '4' && row.course === 'Physics'); - console.log(updatedData) - expect(newEntry).toBeDefined(); - expect(newEntry.grade).toEqual('A'); - - // Cleanup: Delete grades.csv - fs.unlinkSync('grades.csv'); + +test("Execute INSERT INTO Query for grades.csv", async () => { + + // Create grades.csv with initial data + + await createGradesCSV(); + + + + + // Execute INSERT statement + + const insertQuery = + + "INSERT INTO grades (student_id, course, grade) VALUES ('4', 'Physics', 'A')"; + + await executeINSERTQuery(insertQuery); + + + + + // Verify the new entry + + const updatedData = await readCSV("grades.csv"); + + const newEntry = updatedData.find( + + (row) => row.student_id === "4" && row.course === "Physics" + + ); + + + + + + expect(newEntry).toBeDefined(); + + expect(newEntry.grade).toEqual("A"); + + + + + // Cleanup: Delete grades.csv + + fs.unlinkSync("grades.csv"); + }); \ No newline at end of file From 1876454769fd38d379a8572900341b1478d8a813 Mon Sep 17 00:00:00 2001 From: Divy Vinayak Diwedi Date: Sun, 5 May 2024 20:34:26 +0530 Subject: [PATCH 15/15] assignment finished with all test cases passed --- src/csvReader.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/csvReader.js b/src/csvReader.js index 1296d8a25..f07cf3db4 100644 --- a/src/csvReader.js +++ b/src/csvReader.js @@ -2,21 +2,6 @@ const fs = require('fs'); const csv = require('csv-parser'); const { parse } = require('json2csv'); -// function readCSV(filePath) { -// const results = []; - -// return new Promise((resolve, reject) => { -// fs.createReadStream(filePath) -// .pipe(csv()) -// .on('data', (data) => results.push(data)) -// .on('end', () => { -// resolve(results); -// }) -// .on('error', (error) => { -// reject(error); -// }); -// }); -// } function readCSV(filePath) { const results = []; return new Promise((resolve, reject) => {