csv-sql-engine 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE-CC0 +121 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +49 -0
  4. package/dist/augments/trim-lines.d.ts +6 -0
  5. package/dist/augments/trim-lines.js +12 -0
  6. package/dist/csv/csv-file.d.ts +70 -0
  7. package/dist/csv/csv-file.js +98 -0
  8. package/dist/csv/csv-text.d.ts +52 -0
  9. package/dist/csv/csv-text.js +79 -0
  10. package/dist/engine/define-ast-handler.d.ts +27 -0
  11. package/dist/engine/define-ast-handler.js +8 -0
  12. package/dist/engine/engine.d.ts +16 -0
  13. package/dist/engine/engine.js +66 -0
  14. package/dist/engine/handlers/row-delete.handler.d.ts +6 -0
  15. package/dist/engine/handlers/row-delete.handler.js +51 -0
  16. package/dist/engine/handlers/row-insert.handler.d.ts +6 -0
  17. package/dist/engine/handlers/row-insert.handler.js +54 -0
  18. package/dist/engine/handlers/row-select.handler.d.ts +6 -0
  19. package/dist/engine/handlers/row-select.handler.js +46 -0
  20. package/dist/engine/handlers/row-update.handler.d.ts +6 -0
  21. package/dist/engine/handlers/row-update.handler.js +61 -0
  22. package/dist/engine/handlers/table-alter.handler.d.ts +6 -0
  23. package/dist/engine/handlers/table-alter.handler.js +85 -0
  24. package/dist/engine/handlers/table-create.handler.d.ts +6 -0
  25. package/dist/engine/handlers/table-create.handler.js +38 -0
  26. package/dist/engine/handlers/table-drop.handler.d.ts +6 -0
  27. package/dist/engine/handlers/table-drop.handler.js +36 -0
  28. package/dist/engine/params.d.ts +51 -0
  29. package/dist/engine/params.js +1 -0
  30. package/dist/engine/where-matcher.d.ts +9 -0
  31. package/dist/engine/where-matcher.js +52 -0
  32. package/dist/errors/csv-sql-engine.error.d.ts +9 -0
  33. package/dist/errors/csv-sql-engine.error.js +9 -0
  34. package/dist/errors/csv.error.d.ts +57 -0
  35. package/dist/errors/csv.error.js +68 -0
  36. package/dist/errors/sql.error.d.ts +45 -0
  37. package/dist/errors/sql.error.js +64 -0
  38. package/dist/index.d.ts +20 -0
  39. package/dist/index.js +20 -0
  40. package/dist/sql/ast.d.ts +472 -0
  41. package/dist/sql/ast.js +266 -0
  42. package/dist/sql/parse-sql.d.ts +9 -0
  43. package/dist/sql/parse-sql.js +38 -0
  44. package/dist/sql/sql.d.ts +24 -0
  45. package/dist/sql/sql.js +26 -0
  46. package/package.json +99 -11
@@ -0,0 +1,51 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { awaitedBlockingMap } from '@augment-vir/common';
3
+ import { nameCsvTableFile, readCsvFile, readCsvHeaders, writeCsvFile } from '../../csv/csv-file.js';
4
+ import { sortValues } from '../../csv/csv-text.js';
5
+ import { AstType } from '../../sql/ast.js';
6
+ import { defineAstHandler } from '../define-ast-handler.js';
7
+ import { findWhereMatches } from '../where-matcher.js';
8
+ /**
9
+ * Handles deleting rows.
10
+ *
11
+ * @category SQL Handler
12
+ */
13
+ export const rowDeleteHandler = defineAstHandler({
14
+ name: 'row-delete',
15
+ async handler({ ast, csvDirPath }) {
16
+ if (ast.type === AstType.Delete) {
17
+ const tableNames = ast.table.map((table) => table.table);
18
+ const returning = await awaitedBlockingMap(tableNames, async (tableName) => {
19
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
20
+ csvDirPath,
21
+ tableName,
22
+ });
23
+ const csvContents = await readCsvFile(tableFilePath);
24
+ const csvHeaders = await readCsvHeaders({
25
+ csvContents,
26
+ sanitizedTableName,
27
+ });
28
+ const rowIndexesToDelete = findWhereMatches(ast.where, csvContents, tableFilePath);
29
+ const returningRequirement = ast.returning;
30
+ const returning = returningRequirement
31
+ ? csvContents
32
+ .filter((row, index) => rowIndexesToDelete.includes(index))
33
+ .map((row) => sortValues({
34
+ csvFileHeaderOrder: csvHeaders,
35
+ sqlQueryHeaderOrder: returningRequirement.columns.map((column) => column.expr.column),
36
+ from: {
37
+ csvFile: row,
38
+ },
39
+ }))
40
+ : undefined;
41
+ rowIndexesToDelete.forEach((rowIndexToDelete) => {
42
+ csvContents.splice(rowIndexToDelete, 1);
43
+ });
44
+ await writeCsvFile(tableFilePath, csvContents);
45
+ return returning;
46
+ });
47
+ return returning.flat().filter(check.isTruthy);
48
+ }
49
+ return undefined;
50
+ },
51
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Handles inserting rows.
3
+ *
4
+ * @category SQL Handler
5
+ */
6
+ export declare const rowInsertHandler: import("../define-ast-handler.js").AstHandler;
@@ -0,0 +1,54 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { awaitedBlockingMap } from '@augment-vir/common';
3
+ import { appendCsvRow, nameCsvTableFile, readCsvHeaders } from '../../csv/csv-file.js';
4
+ import { sortValues } from '../../csv/csv-text.js';
5
+ import { AstType } from '../../sql/ast.js';
6
+ import { defineAstHandler } from '../define-ast-handler.js';
7
+ /**
8
+ * Handles inserting rows.
9
+ *
10
+ * @category SQL Handler
11
+ */
12
+ export const rowInsertHandler = defineAstHandler({
13
+ name: 'row-insert',
14
+ async handler({ ast, csvDirPath }) {
15
+ if (ast.type === AstType.Insert) {
16
+ const tableNames = ast.table.map((table) => table.table);
17
+ const returning = await awaitedBlockingMap(tableNames, async (tableName) => {
18
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
19
+ csvDirPath,
20
+ tableName,
21
+ });
22
+ const rawValues = ast.values.values.flatMap((value) => value.value.flatMap((value) => String(value.value)));
23
+ const csvFileHeaderOrder = await readCsvHeaders({
24
+ csvFilePath: tableFilePath,
25
+ sanitizedTableName,
26
+ });
27
+ const values = ast.columns
28
+ ? sortValues({
29
+ csvFileHeaderOrder,
30
+ sqlQueryHeaderOrder: ast.columns,
31
+ from: {
32
+ sqlQuery: rawValues,
33
+ },
34
+ })
35
+ : rawValues;
36
+ await appendCsvRow(values, tableFilePath);
37
+ if (ast.returning) {
38
+ return sortValues({
39
+ csvFileHeaderOrder,
40
+ sqlQueryHeaderOrder: ast.returning.columns.map((column) => column.expr.column),
41
+ from: {
42
+ csvFile: values,
43
+ },
44
+ });
45
+ }
46
+ else {
47
+ return undefined;
48
+ }
49
+ });
50
+ return returning.filter(check.isTruthy);
51
+ }
52
+ return undefined;
53
+ },
54
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Handles SQL selection.
3
+ *
4
+ * @category SQL Handler
5
+ */
6
+ export declare const rowSelectHandler: import("../define-ast-handler.js").AstHandler;
@@ -0,0 +1,46 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { awaitedBlockingMap } from '@augment-vir/common';
3
+ import { nameCsvTableFile, readCsvFile, readCsvHeaders, writeCsvFile } from '../../csv/csv-file.js';
4
+ import { sortValues } from '../../csv/csv-text.js';
5
+ import { AstType } from '../../sql/ast.js';
6
+ import { defineAstHandler } from '../define-ast-handler.js';
7
+ import { findWhereMatches } from '../where-matcher.js';
8
+ /**
9
+ * Handles SQL selection.
10
+ *
11
+ * @category SQL Handler
12
+ */
13
+ export const rowSelectHandler = defineAstHandler({
14
+ name: 'row-select',
15
+ async handler({ ast, csvDirPath }) {
16
+ if (ast.type === AstType.Select) {
17
+ const tableNames = ast.from.map((table) => table.table);
18
+ const allSelections = await awaitedBlockingMap(tableNames, async (tableName) => {
19
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
20
+ csvDirPath,
21
+ tableName,
22
+ });
23
+ const csvContents = await readCsvFile(tableFilePath);
24
+ const csvHeaders = await readCsvHeaders({
25
+ csvContents,
26
+ sanitizedTableName,
27
+ });
28
+ const rowIndexesToSelect = findWhereMatches(ast.where, csvContents, tableFilePath);
29
+ const columnNames = ast.columns.map((column) => column.expr.column);
30
+ const selection = csvContents
31
+ .filter((row, index) => rowIndexesToSelect.includes(index))
32
+ .map((row) => sortValues({
33
+ csvFileHeaderOrder: csvHeaders,
34
+ sqlQueryHeaderOrder: columnNames,
35
+ from: {
36
+ csvFile: row,
37
+ },
38
+ }));
39
+ await writeCsvFile(tableFilePath, csvContents);
40
+ return selection;
41
+ });
42
+ return allSelections.flat().filter(check.isTruthy);
43
+ }
44
+ return undefined;
45
+ },
46
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Handles updating rows.
3
+ *
4
+ * @category SQL Handler
5
+ */
6
+ export declare const rowUpdateHandler: import("../define-ast-handler.js").AstHandler;
@@ -0,0 +1,61 @@
1
+ import { assertWrap, check } from '@augment-vir/assert';
2
+ import { awaitedBlockingMap } from '@augment-vir/common';
3
+ import { createCsvHeaderMaps, nameCsvTableFile, readCsvFile, readCsvHeaders, writeCsvFile, } from '../../csv/csv-file.js';
4
+ import { sortValues } from '../../csv/csv-text.js';
5
+ import { CsvColumnDoesNotExistError } from '../../errors/csv.error.js';
6
+ import { AstType } from '../../sql/ast.js';
7
+ import { defineAstHandler } from '../define-ast-handler.js';
8
+ import { findWhereMatches } from '../where-matcher.js';
9
+ /**
10
+ * Handles updating rows.
11
+ *
12
+ * @category SQL Handler
13
+ */
14
+ export const rowUpdateHandler = defineAstHandler({
15
+ name: 'row-update',
16
+ async handler({ ast, csvDirPath }) {
17
+ if (ast.type === AstType.Update) {
18
+ const tableNames = ast.table.map((table) => table.table);
19
+ const returning = await awaitedBlockingMap(tableNames, async (tableName) => {
20
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
21
+ csvDirPath,
22
+ tableName,
23
+ });
24
+ const csvContents = await readCsvFile(tableFilePath);
25
+ const csvHeaders = await readCsvHeaders({
26
+ csvContents,
27
+ sanitizedTableName,
28
+ });
29
+ const csvHeaderIndexes = createCsvHeaderMaps(csvHeaders);
30
+ const rowIndexesToUpdate = findWhereMatches(ast.where, csvContents, tableFilePath);
31
+ const returningRequirement = ast.returning;
32
+ rowIndexesToUpdate.forEach((rowIndexToUpdate) => {
33
+ const row = assertWrap.isDefined(csvContents[rowIndexToUpdate], `Invalid row index '${rowIndexToUpdate}'.`);
34
+ ast.set.forEach((set) => {
35
+ const columnName = set.column;
36
+ const headerIndex = csvHeaderIndexes.byName[columnName];
37
+ if (!headerIndex) {
38
+ throw new CsvColumnDoesNotExistError(sanitizedTableName, columnName);
39
+ }
40
+ row[headerIndex] = set.value.value;
41
+ });
42
+ });
43
+ const returning = returningRequirement
44
+ ? csvContents
45
+ .filter((row, index) => rowIndexesToUpdate.includes(index))
46
+ .map((row) => sortValues({
47
+ csvFileHeaderOrder: csvHeaders,
48
+ sqlQueryHeaderOrder: returningRequirement.columns.map((column) => column.expr.column),
49
+ from: {
50
+ csvFile: row,
51
+ },
52
+ }))
53
+ : undefined;
54
+ await writeCsvFile(tableFilePath, csvContents);
55
+ return returning;
56
+ });
57
+ return returning.flat().filter(check.isTruthy);
58
+ }
59
+ return undefined;
60
+ },
61
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Handles altering tables.
3
+ *
4
+ * @category SQL Handler
5
+ */
6
+ export declare const tableAlterHandler: import("../define-ast-handler.js").AstHandler;
@@ -0,0 +1,85 @@
1
+ import { assert } from '@augment-vir/assert';
2
+ import { awaitedForEach, stringify } from '@augment-vir/common';
3
+ import { existsSync } from 'node:fs';
4
+ import { nameCsvTableFile, readCsvFile, readCsvHeaders, writeCsvFile } from '../../csv/csv-file.js';
5
+ import { CsvColumnDoesNotExistError, CsvTableDoesNotExistError } from '../../errors/csv.error.js';
6
+ import { AlterExpressionAction } from '../../sql/ast.js';
7
+ import { defineAstHandler } from '../define-ast-handler.js';
8
+ /**
9
+ * Handles altering tables.
10
+ *
11
+ * @category SQL Handler
12
+ */
13
+ export const tableAlterHandler = defineAstHandler({
14
+ name: 'table-alter',
15
+ async handler({ ast, csvDirPath }) {
16
+ if (ast.type === 'alter') {
17
+ const tableNames = ast.table.map((table) => table.table);
18
+ await awaitedForEach(tableNames, async (tableName) => {
19
+ await awaitedForEach(ast.expr, async (expression) => {
20
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
21
+ csvDirPath,
22
+ tableName,
23
+ });
24
+ if (!existsSync(tableFilePath)) {
25
+ throw new CsvTableDoesNotExistError(sanitizedTableName);
26
+ }
27
+ /** Mutate this to apply the table alterations. */
28
+ const csvContents = await readCsvFile(tableFilePath);
29
+ const csvHeaders = await readCsvHeaders({
30
+ csvContents,
31
+ sanitizedTableName,
32
+ });
33
+ if (expression.action === AlterExpressionAction.Add) {
34
+ const defaultValue = expression.default_val?.value.value || '';
35
+ const newHeaderName = expression.column.column;
36
+ csvContents.forEach((row, index) => {
37
+ if (index) {
38
+ row.push(defaultValue);
39
+ }
40
+ else {
41
+ row.push(newHeaderName);
42
+ }
43
+ });
44
+ }
45
+ else if (expression.action === AlterExpressionAction.Rename) {
46
+ const oldHeaderName = expression.old_column.column;
47
+ const newHeaderName = expression.column.column;
48
+ let oldHeaderFound = false;
49
+ const newHeaders = csvHeaders.map((header) => {
50
+ if (header === oldHeaderName) {
51
+ oldHeaderFound = true;
52
+ return newHeaderName;
53
+ }
54
+ else {
55
+ return header;
56
+ }
57
+ });
58
+ if (!oldHeaderFound) {
59
+ throw new CsvColumnDoesNotExistError(sanitizedTableName, oldHeaderName);
60
+ }
61
+ csvContents[0] = newHeaders;
62
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
63
+ }
64
+ else if (expression.action === AlterExpressionAction.Drop) {
65
+ const columnName = expression.column.column;
66
+ const columnIndex = csvHeaders.indexOf(columnName);
67
+ if (columnIndex < 0) {
68
+ throw new CsvColumnDoesNotExistError(sanitizedTableName, columnName);
69
+ }
70
+ csvContents.forEach((row) => {
71
+ row.splice(columnIndex, 1);
72
+ });
73
+ }
74
+ else {
75
+ assert.tsType(expression).equals();
76
+ assert.never(`Forgot to handle expression action ${stringify(expression)}`);
77
+ }
78
+ await writeCsvFile(tableFilePath, csvContents);
79
+ });
80
+ });
81
+ return [];
82
+ }
83
+ return undefined;
84
+ },
85
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Handles creating tables.
3
+ *
4
+ * @category SQL Handler
5
+ */
6
+ export declare const tableCreateHandler: import("../define-ast-handler.js").AstHandler;
@@ -0,0 +1,38 @@
1
+ import { check } from '@augment-vir/assert';
2
+ import { awaitedForEach, filterMap } from '@augment-vir/common';
3
+ import { existsSync } from 'node:fs';
4
+ import { appendCsvRow, nameCsvTableFile } from '../../csv/csv-file.js';
5
+ import { CsvTableExistsError } from '../../errors/csv.error.js';
6
+ import { SqlMissingColumnsError } from '../../errors/sql.error.js';
7
+ import { AstType, CreateKeyword } from '../../sql/ast.js';
8
+ import { defineAstHandler } from '../define-ast-handler.js';
9
+ /**
10
+ * Handles creating tables.
11
+ *
12
+ * @category SQL Handler
13
+ */
14
+ export const tableCreateHandler = defineAstHandler({
15
+ name: 'table-create',
16
+ async handler({ ast, csvDirPath, sql }) {
17
+ if (ast.type === AstType.Create && ast.keyword === CreateKeyword.Table) {
18
+ await awaitedForEach(ast.table, async (table) => {
19
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
20
+ csvDirPath,
21
+ tableName: table.table,
22
+ });
23
+ if (existsSync(tableFilePath)) {
24
+ throw new CsvTableExistsError(sanitizedTableName);
25
+ }
26
+ const headers = filterMap(ast.create_definitions, (definition) => {
27
+ return definition.column.column;
28
+ }, check.isTruthy);
29
+ if (!headers.length) {
30
+ throw new SqlMissingColumnsError(sql, sanitizedTableName);
31
+ }
32
+ await appendCsvRow(headers, tableFilePath);
33
+ });
34
+ return [];
35
+ }
36
+ return undefined;
37
+ },
38
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Handles dropping tables.
3
+ *
4
+ * @category SQL Handler
5
+ */
6
+ export declare const tableDropHandler: import("../define-ast-handler.js").AstHandler;
@@ -0,0 +1,36 @@
1
+ import { awaitedForEach } from '@augment-vir/common';
2
+ import { existsSync } from 'node:fs';
3
+ import { rm } from 'node:fs/promises';
4
+ import { nameCsvTableFile } from '../../csv/csv-file.js';
5
+ import { CsvTableDoesNotExistError } from '../../errors/csv.error.js';
6
+ import { AstType } from '../../sql/ast.js';
7
+ import { defineAstHandler } from '../define-ast-handler.js';
8
+ /**
9
+ * Handles dropping tables.
10
+ *
11
+ * @category SQL Handler
12
+ */
13
+ export const tableDropHandler = defineAstHandler({
14
+ name: 'table-drop',
15
+ async handler({ ast, csvDirPath }) {
16
+ if (ast.type === AstType.Drop) {
17
+ await awaitedForEach(ast.name, async (table) => {
18
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
19
+ csvDirPath,
20
+ tableName: table.table,
21
+ });
22
+ if (existsSync(tableFilePath)) {
23
+ await rm(tableFilePath);
24
+ }
25
+ else if (ast.prefix === 'if exists') {
26
+ return;
27
+ }
28
+ else {
29
+ throw new CsvTableDoesNotExistError(sanitizedTableName);
30
+ }
31
+ });
32
+ return [];
33
+ }
34
+ return undefined;
35
+ },
36
+ });
@@ -0,0 +1,51 @@
1
+ import { type PartialWithUndefined } from '@augment-vir/common';
2
+ import { type Parser } from 'node-sql-parser';
3
+ import { type SqlAst } from '../sql/ast.js';
4
+ import { type Sql } from '../sql/sql.js';
5
+ /**
6
+ * Options for rejecting unsupported operations (rather than ignoring them).
7
+ *
8
+ * @category Internal
9
+ */
10
+ export type RejectUnsupportedOptions = PartialWithUndefined<{
11
+ /**
12
+ * - If set to `true`, unsupported operations will throw errors.
13
+ * - When left to `false`, unsupported operations are simply ignored.
14
+ *
15
+ * @default false
16
+ */
17
+ rejectUnsupportedOperations: boolean;
18
+ }>;
19
+ /**
20
+ * Options for parsing SQL.
21
+ *
22
+ * @category Internal
23
+ */
24
+ export type ParseSqlOptions = PartialWithUndefined<{
25
+ /**
26
+ * A custom implementation of the Parser from
27
+ * [node-sql-parser](https://npmjs.com/package/node-sql-parser). If not provided, one will be
28
+ * constructed.
29
+ */
30
+ parser: Parser;
31
+ }> & RejectUnsupportedOptions;
32
+ /**
33
+ * Parameters for AST handlers.
34
+ *
35
+ * @category Internal
36
+ */
37
+ export type AstHandlerParams = {
38
+ ast: SqlAst;
39
+ sql: Sql;
40
+ /**
41
+ * Path to the folder or directory containing all the CSV files. (Each CSV file is treated as an
42
+ * individual table.)
43
+ */
44
+ csvDirPath: string;
45
+ } & RejectUnsupportedOptions;
46
+ /**
47
+ * Parameters for executing SQL.
48
+ *
49
+ * @category Internal
50
+ */
51
+ export type ExecuteSqlParams = ParseSqlOptions & Omit<AstHandlerParams, 'ast' | 'sql'>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import { type CsvFile } from '../csv/csv-file.js';
2
+ import { type Where } from '../sql/ast.js';
3
+ /**
4
+ * Finds all row indexes that match the given SQL where conditions.
5
+ *
6
+ * @category Internal
7
+ * @returns An array of row indexes that match the given where condition.
8
+ */
9
+ export declare function findWhereMatches(where: Readonly<Where>, csvContents: Readonly<CsvFile>, csvFilePath: string): number[];
@@ -0,0 +1,52 @@
1
+ import { assert } from '@augment-vir/assert';
2
+ import { extractDuplicates, filterMap, removeDuplicates, removeSuffix } from '@augment-vir/common';
3
+ import { csvExtension } from '../csv/csv-file.js';
4
+ import { CsvColumnDoesNotExistError, CsvFileMissingHeadersError } from '../errors/csv.error.js';
5
+ import { WhereOperator } from '../sql/ast.js';
6
+ /**
7
+ * Finds all row indexes that match the given SQL where conditions.
8
+ *
9
+ * @category Internal
10
+ * @returns An array of row indexes that match the given where condition.
11
+ */
12
+ export function findWhereMatches(where, csvContents, csvFilePath) {
13
+ /**
14
+ * These must be sorted from greatest to least so that deleting rows does not mess up the
15
+ * indexes.
16
+ */
17
+ return innerFindWhereMatches(where, csvContents, csvFilePath).sort((a, b) => b - a);
18
+ }
19
+ function innerFindWhereMatches(where, csvContents, csvFilePath) {
20
+ if (where.operator === WhereOperator.Or) {
21
+ return removeDuplicates([
22
+ ...innerFindWhereMatches(where.left, csvContents, csvFilePath),
23
+ ...innerFindWhereMatches(where.right, csvContents, csvFilePath),
24
+ ]);
25
+ }
26
+ else if (where.operator === WhereOperator.And) {
27
+ return extractDuplicates([
28
+ ...innerFindWhereMatches(where.left, csvContents, csvFilePath),
29
+ ...innerFindWhereMatches(where.right, csvContents, csvFilePath),
30
+ ]).duplicates;
31
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
32
+ }
33
+ else if (where.operator === WhereOperator.Equals) {
34
+ const headers = csvContents[0];
35
+ if (!headers) {
36
+ throw new CsvFileMissingHeadersError(csvFilePath);
37
+ }
38
+ const columnIndex = headers.indexOf(where.left.column);
39
+ if (columnIndex < 0) {
40
+ throw new CsvColumnDoesNotExistError(removeSuffix({ value: csvFilePath, suffix: csvExtension }), where.left.column);
41
+ }
42
+ return filterMap(csvContents, (row, index) => index, (index, row) => {
43
+ /** Don't select from the header row. */
44
+ const isHeaderRow = !index;
45
+ return !isHeaderRow && String(row[columnIndex]) === String(where.right.value);
46
+ });
47
+ }
48
+ else {
49
+ assert.tsType(where.operator).equals();
50
+ throw new Error(`Forgot to implement WHERE operation: '${String(where.operator)}'`);
51
+ }
52
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generic error thrown by the csv-sql-engine package. All known errors from this package extend
3
+ * this error class.
4
+ *
5
+ * @category Error
6
+ */
7
+ export declare class CsvSqlEngineError extends Error {
8
+ readonly name: string;
9
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generic error thrown by the csv-sql-engine package. All known errors from this package extend
3
+ * this error class.
4
+ *
5
+ * @category Error
6
+ */
7
+ export class CsvSqlEngineError extends Error {
8
+ name = 'CsvSqlEngineError';
9
+ }
@@ -0,0 +1,57 @@
1
+ import { CsvSqlEngineError } from './csv-sql-engine.error.js';
2
+ /**
3
+ * Generic CSV related error thrown by the csv-sql-engine package. All CSV related errors from this
4
+ * package extend this error class.
5
+ *
6
+ * @category Error
7
+ */
8
+ export declare class CsvError extends CsvSqlEngineError {
9
+ readonly name: string;
10
+ }
11
+ /**
12
+ * Indicates that an table CSV file already exists when it shouldn't.
13
+ *
14
+ * @category Error
15
+ */
16
+ export declare class CsvTableExistsError extends CsvError {
17
+ readonly name: string;
18
+ constructor(tableName: string);
19
+ }
20
+ /**
21
+ * Indicates that an expected table CSV file does not actually exist.
22
+ *
23
+ * @category Error
24
+ */
25
+ export declare class CsvTableDoesNotExistError extends CsvError {
26
+ readonly name: string;
27
+ constructor(tableName: string);
28
+ }
29
+ /**
30
+ * Indicates that parsing of an existing CSV file failed.
31
+ *
32
+ * @category Error
33
+ */
34
+ export declare class CsvParseError extends CsvError {
35
+ readonly name: string;
36
+ }
37
+ /**
38
+ * Indicates that a CSV file is missing headers.
39
+ *
40
+ * @category Error
41
+ */
42
+ export declare class CsvFileMissingHeadersError extends CsvParseError {
43
+ readonly csvFilePath: string;
44
+ readonly name: string;
45
+ constructor(csvFilePath: string);
46
+ }
47
+ /**
48
+ * Indicates that an expected column in a CSV file does not actually exist.
49
+ *
50
+ * @category Error
51
+ */
52
+ export declare class CsvColumnDoesNotExistError extends CsvError {
53
+ readonly sanitizedTableName: string;
54
+ readonly missingColumName: string;
55
+ readonly name: string;
56
+ constructor(sanitizedTableName: string, missingColumName: string);
57
+ }