csv-sql-engine 0.1.0 → 0.3.0

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.
@@ -1,13 +1,13 @@
1
1
  import { type MaybePromise } from '@augment-vir/common';
2
2
  import { type AstHandlerParams } from './params.js';
3
+ import { type SortValuesOutput } from './sort-values.js';
3
4
  /**
4
5
  * Output from a handler that handled a SQL query.
5
6
  *
6
7
  * @category Internal
7
8
  */
8
- export type AstHandlerResult = {
9
- columnNames: string[];
10
- values: string[][];
9
+ export type AstHandlerResult = SortValuesOutput & {
10
+ numberOfRowsAffected: number;
11
11
  };
12
12
  /**
13
13
  * An AST / SQL handler.
@@ -20,7 +20,7 @@ export type AstHandler = {
20
20
  * Return `undefined` to mark this AST as not-handled. That means that other handlers should be
21
21
  * used instead.
22
22
  */
23
- handler: (params: Readonly<AstHandlerParams>) => MaybePromise<AstHandlerResult[] | undefined>;
23
+ handler: (params: Readonly<AstHandlerParams>) => MaybePromise<AstHandlerResult | undefined>;
24
24
  };
25
25
  /**
26
26
  * Used to define new handlers.
@@ -1,4 +1,4 @@
1
- import { type Sql } from '../sql/sql.js';
1
+ import { type Sql } from 'sqlite-ast';
2
2
  import { type AstHandler, type AstHandlerResult } from './define-ast-handler.js';
3
3
  import { type ExecuteSqlParams } from './params.js';
4
4
  /**
@@ -13,4 +13,4 @@ export declare const allAstHandlers: ReadonlyArray<Readonly<AstHandler>>;
13
13
  *
14
14
  * @category Main
15
15
  */
16
- export declare function executeSql(sqlInput: Sql | string, params: Readonly<ExecuteSqlParams>): Promise<AstHandlerResult[][]>;
16
+ export declare function executeSql(sqlInput: Sql | string, params: Readonly<ExecuteSqlParams>): Promise<AstHandlerResult[]>;
@@ -1,9 +1,8 @@
1
1
  import { check } from '@augment-vir/assert';
2
- import { awaitedBlockingMap, ensureErrorAndPrependMessage } from '@augment-vir/common';
2
+ import { awaitedBlockingMap, ensureErrorAndPrependMessage, wrapInTry, } from '@augment-vir/common';
3
3
  import { mkdir } from 'node:fs/promises';
4
- import { SqlUnsupportedOperationError } from '../errors/sql.error.js';
5
- import { parseSql } from '../sql/parse-sql.js';
6
- import { rawSql } from '../sql/sql.js';
4
+ import { parseSqlite, rawSql } from 'sqlite-ast';
5
+ import { SqlParseError, SqlUnsupportedOperationError } from '../errors/sql.error.js';
7
6
  import { rowDeleteHandler } from './handlers/row-delete.handler.js';
8
7
  import { rowInsertHandler } from './handlers/row-insert.handler.js';
9
8
  import { rowSelectHandler } from './handlers/row-select.handler.js';
@@ -34,33 +33,38 @@ export const allAstHandlers = [
34
33
  export async function executeSql(sqlInput, params) {
35
34
  // eslint-disable-next-line @typescript-eslint/no-deprecated
36
35
  const sql = check.isString(sqlInput) ? rawSql(sqlInput) : sqlInput;
37
- const astResults = parseSql(sql, params);
36
+ const astList = wrapInTry(() => parseSqlite(sql), {
37
+ handleError(error) {
38
+ throw new SqlParseError(sql, error);
39
+ },
40
+ });
38
41
  await mkdir(params.csvDirPath, {
39
42
  recursive: true,
40
43
  });
41
- return await awaitedBlockingMap(astResults, async (ast) => {
44
+ return (await awaitedBlockingMap(astList, async (ast) => {
42
45
  return await executeIndividualCommand({
43
46
  ...params,
44
47
  ast,
45
48
  sql,
46
49
  });
47
- });
50
+ })).filter(check.isTruthy);
48
51
  }
49
52
  async function executeIndividualCommand(params) {
50
53
  try {
51
54
  for (const handler of allAstHandlers) {
52
55
  const output = await handler.handler(params);
53
56
  if (output) {
54
- return output.filter((entry) => entry.values.length);
57
+ return output;
55
58
  }
56
59
  }
57
60
  /** If nothing handled the query, then we don't support it. */
58
61
  if (params.rejectUnsupportedOperations) {
59
- throw new SqlUnsupportedOperationError(params.sql, undefined);
62
+ throw new SqlUnsupportedOperationError(params.sql, undefined, params.ast);
60
63
  }
61
- return [];
64
+ return undefined;
62
65
  }
63
66
  catch (error) {
64
- throw ensureErrorAndPrependMessage(error, `Failed to execute '${params.ast.type}' command.`);
67
+ const errorAst = params.ast;
68
+ throw ensureErrorAndPrependMessage(error, `Failed to execute '${errorAst.variant || errorAst.type}' command.`);
65
69
  }
66
70
  }
@@ -1,7 +1,5 @@
1
- import { check } from '@augment-vir/assert';
2
- import { awaitedBlockingMap } from '@augment-vir/common';
3
1
  import { nameCsvTableFile, readCsvFile, readCsvHeaders, writeCsvFile } from '../../csv/csv-file.js';
4
- import { AstType } from '../../sql/ast.js';
2
+ import { getAstType } from '../../util/ast-node.js';
5
3
  import { defineAstHandler } from '../define-ast-handler.js';
6
4
  import { sortValues } from '../sort-values.js';
7
5
  import { findWhereMatches } from '../where-matcher.js';
@@ -13,38 +11,53 @@ import { findWhereMatches } from '../where-matcher.js';
13
11
  export const rowDeleteHandler = defineAstHandler({
14
12
  name: 'row-delete',
15
13
  async handler({ ast, csvDirPath, sql }) {
16
- if (ast.type === AstType.Delete) {
17
- const tableNames = ast.table.map((table) => table.table);
18
- const results = 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 result = returningRequirement
31
- ? sortValues({
32
- csvFileHeaderOrder: csvHeaders,
33
- sqlQueryHeaderOrder: returningRequirement.columns.map((column) => column.expr.column),
34
- from: {
35
- csvFile: csvContents.filter((row, index) => rowIndexesToDelete.includes(index)),
36
- },
37
- unconsumedInterpolationValues: sql.unconsumedValues,
38
- })
39
- : undefined;
40
- rowIndexesToDelete.forEach((rowIndexToDelete) => {
41
- csvContents.splice(rowIndexToDelete, 1);
42
- });
43
- await writeCsvFile(tableFilePath, csvContents);
44
- return result;
45
- });
46
- return results.flat().filter(check.isTruthy);
14
+ if (ast.variant !== 'delete') {
15
+ return undefined;
47
16
  }
48
- return undefined;
17
+ const tableName = getAstType(ast.from, 'identifier')?.name;
18
+ if (!tableName) {
19
+ throw new Error('Missing table name.');
20
+ }
21
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
22
+ csvDirPath,
23
+ tableName,
24
+ });
25
+ const csvContents = await readCsvFile(tableFilePath);
26
+ const csvHeaders = await readCsvHeaders({
27
+ csvContents,
28
+ sanitizedTableName,
29
+ });
30
+ const rowIndexesToDelete = findWhereMatches(ast.where, csvContents, tableFilePath);
31
+ const returningRequirement = ast.returning;
32
+ const sqlHeaders = returningRequirement?.map((column) => {
33
+ const columnNode = getAstType(column, 'identifier');
34
+ if (columnNode) {
35
+ return columnNode.name;
36
+ }
37
+ else {
38
+ throw new Error(`Unexpected return column type: ${column.type}`);
39
+ }
40
+ }) || [];
41
+ const result = returningRequirement
42
+ ? sortValues({
43
+ csvFileHeaderOrder: csvHeaders,
44
+ sqlQueryHeaderOrder: sqlHeaders,
45
+ from: {
46
+ csvFile: csvContents.filter((row, index) => rowIndexesToDelete.includes(index)),
47
+ },
48
+ unconsumedInterpolationValues: sql.unconsumedValues,
49
+ })
50
+ : {
51
+ columnNames: [],
52
+ values: [],
53
+ };
54
+ rowIndexesToDelete.forEach((rowIndexToDelete) => {
55
+ csvContents.splice(rowIndexToDelete, 1);
56
+ });
57
+ await writeCsvFile(tableFilePath, csvContents);
58
+ return {
59
+ ...result,
60
+ numberOfRowsAffected: rowIndexesToDelete.length,
61
+ };
49
62
  },
50
63
  });
@@ -1,7 +1,9 @@
1
1
  import { assertWrap, check } from '@augment-vir/assert';
2
- import { awaitedBlockingMap } from '@augment-vir/common';
3
- import { appendCsvRow, nameCsvTableFile, readCsvHeaders } from '../../csv/csv-file.js';
4
- import { AstType } from '../../sql/ast.js';
2
+ import { filterMap } from '@augment-vir/common';
3
+ import { existsSync } from 'node:fs';
4
+ import { appendCsvRow, nameCsvTableFile, readCsvFile, readCsvHeaders } from '../../csv/csv-file.js';
5
+ import { CsvTableDoesNotExistError } from '../../errors/csv.error.js';
6
+ import { SqlUnsupportedOperationError } from '../../errors/sql.error.js';
5
7
  import { defineAstHandler } from '../define-ast-handler.js';
6
8
  import { sortValues } from '../sort-values.js';
7
9
  /**
@@ -11,45 +13,107 @@ import { sortValues } from '../sort-values.js';
11
13
  */
12
14
  export const rowInsertHandler = defineAstHandler({
13
15
  name: 'row-insert',
14
- async handler({ ast, csvDirPath, sql }) {
15
- if (ast.type === AstType.Insert) {
16
- const tableNames = ast.table.map((table) => table.table);
17
- const results = 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 newRow = assertWrap.isDefined(sortValues({
28
- csvFileHeaderOrder,
29
- sqlQueryHeaderOrder: ast.columns || csvFileHeaderOrder,
30
- from: {
31
- sqlQuery: [rawValues],
32
- },
33
- unconsumedInterpolationValues: sql.unconsumedValues,
34
- }).values[0], 'No sorted row retrieved.');
35
- await appendCsvRow(newRow, tableFilePath);
36
- if (ast.returning) {
37
- const sqlHeaders = ast.returning.columns.map((column) => column.expr.column);
38
- return sortValues({
39
- csvFileHeaderOrder,
40
- sqlQueryHeaderOrder: sqlHeaders,
41
- from: {
42
- csvFile: [newRow],
43
- },
44
- unconsumedInterpolationValues: undefined,
45
- });
16
+ async handler({ ast, csvDirPath, sql, rejectUnsupportedOperations }) {
17
+ if (ast.variant !== 'insert' ||
18
+ ast.into.type !== 'identifier' ||
19
+ (ast.into.variant !== 'table' && ast.into.format !== 'table')) {
20
+ return;
21
+ }
22
+ const tableName = ast.into.name;
23
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
24
+ csvDirPath,
25
+ tableName,
26
+ });
27
+ if (!existsSync(tableFilePath)) {
28
+ throw new CsvTableDoesNotExistError(sanitizedTableName);
29
+ }
30
+ /** Mutate this to apply the table alterations. */
31
+ const csvContents = await readCsvFile(tableFilePath);
32
+ const csvHeaders = await readCsvHeaders({
33
+ csvContents,
34
+ sanitizedTableName,
35
+ });
36
+ const sqlHeaders = ast.into.columns
37
+ ? filterMap(ast.into.columns, (column) => {
38
+ if (column.type === 'identifier' && column.variant === 'column') {
39
+ return column.name;
40
+ }
41
+ else if (rejectUnsupportedOperations) {
42
+ throw new SqlUnsupportedOperationError(sql, `Unsupported column definition: '${JSON.stringify(column)}'`, ast);
46
43
  }
47
44
  else {
48
45
  return undefined;
49
46
  }
50
- });
51
- return results.filter(check.isTruthy);
47
+ }, check.isTruthy)
48
+ : csvHeaders;
49
+ if (!check.isArray(ast.result)) {
50
+ if (rejectUnsupportedOperations) {
51
+ throw new SqlUnsupportedOperationError(sql, `Unsupported result: '${JSON.stringify(ast.result)}'`, ast);
52
+ }
53
+ else {
54
+ return undefined;
55
+ }
56
+ }
57
+ const resultExpression = assertWrap.isTruthy(ast.result[0], 'No result.');
58
+ if (resultExpression.type !== 'expression' ||
59
+ resultExpression.variant !== 'list' ||
60
+ !check.isArray(resultExpression.expression)) {
61
+ if (rejectUnsupportedOperations) {
62
+ throw new SqlUnsupportedOperationError(sql, `Expected expression: ${JSON.stringify(resultExpression)}`, ast);
63
+ }
64
+ else {
65
+ return undefined;
66
+ }
52
67
  }
53
- return undefined;
68
+ const rawValues = filterMap(resultExpression.expression, (entry) => {
69
+ if (entry.type === 'identifier' || entry.type === 'variable') {
70
+ return entry.name;
71
+ }
72
+ else if (entry.type === 'literal') {
73
+ return entry.value;
74
+ }
75
+ else if (rejectUnsupportedOperations) {
76
+ throw new SqlUnsupportedOperationError(sql, `Unsupported expression entry: ${JSON.stringify(entry)}`, ast);
77
+ }
78
+ else {
79
+ return undefined;
80
+ }
81
+ }, check.isTruthy);
82
+ const newRow = assertWrap.isTruthy(sortValues({
83
+ csvFileHeaderOrder: csvHeaders,
84
+ sqlQueryHeaderOrder: sqlHeaders,
85
+ unconsumedInterpolationValues: sql.unconsumedValues,
86
+ from: {
87
+ sqlQuery: [rawValues],
88
+ },
89
+ }).values[0], 'No sorted row retrieved.');
90
+ await appendCsvRow(newRow, tableFilePath);
91
+ const readResult = ast.returning
92
+ ? sortValues({
93
+ csvFileHeaderOrder: csvHeaders,
94
+ sqlQueryHeaderOrder: filterMap(ast.returning, (entry) => {
95
+ if (entry.type === 'identifier') {
96
+ return entry.name;
97
+ }
98
+ else if (rejectUnsupportedOperations) {
99
+ throw new Error(`Unsupported returning entry: ${JSON.stringify(entry)}`);
100
+ }
101
+ else {
102
+ return undefined;
103
+ }
104
+ }, check.isTruthy),
105
+ from: {
106
+ csvFile: [newRow],
107
+ },
108
+ unconsumedInterpolationValues: undefined,
109
+ })
110
+ : {
111
+ columnNames: [],
112
+ values: [],
113
+ };
114
+ return {
115
+ ...readResult,
116
+ numberOfRowsAffected: 1,
117
+ };
54
118
  },
55
119
  });
@@ -1,7 +1,8 @@
1
1
  import { check } from '@augment-vir/assert';
2
- import { awaitedBlockingMap } from '@augment-vir/common';
2
+ import { filterMap } from '@augment-vir/common';
3
3
  import { nameCsvTableFile, readCsvFile, readCsvHeaders } from '../../csv/csv-file.js';
4
- import { AstType } from '../../sql/ast.js';
4
+ import { SqlUnsupportedOperationError } from '../../errors/sql.error.js';
5
+ import { getAstType } from '../../util/ast-node.js';
5
6
  import { defineAstHandler } from '../define-ast-handler.js';
6
7
  import { sortValues } from '../sort-values.js';
7
8
  import { findWhereMatches } from '../where-matcher.js';
@@ -12,32 +13,47 @@ import { findWhereMatches } from '../where-matcher.js';
12
13
  */
13
14
  export const rowSelectHandler = defineAstHandler({
14
15
  name: 'row-select',
15
- async handler({ ast, csvDirPath, sql }) {
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
- return sortValues({
31
- csvFileHeaderOrder: csvHeaders,
32
- sqlQueryHeaderOrder: columnNames,
33
- from: {
34
- csvFile: csvContents.filter((row, index) => rowIndexesToSelect.includes(index)),
35
- },
36
- unconsumedInterpolationValues: sql.unconsumedValues,
37
- });
38
- });
39
- return allSelections.flat().filter(check.isTruthy);
16
+ async handler({ ast, csvDirPath, sql, rejectUnsupportedOperations }) {
17
+ if (ast.variant !== 'select') {
18
+ return undefined;
40
19
  }
41
- return undefined;
20
+ const tableName = getAstType(ast.from, 'identifier')?.name;
21
+ if (!tableName) {
22
+ throw new Error('No table name.');
23
+ }
24
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
25
+ csvDirPath,
26
+ tableName,
27
+ });
28
+ const csvContents = await readCsvFile(tableFilePath);
29
+ const csvHeaders = await readCsvHeaders({
30
+ csvContents,
31
+ sanitizedTableName,
32
+ });
33
+ const rowIndexesToSelect = findWhereMatches(ast.where, csvContents, tableFilePath);
34
+ const columnNames = filterMap(ast.result, (result) => {
35
+ return getAstType(result, 'identifier')?.name;
36
+ }, (value) => {
37
+ if (check.isString(value)) {
38
+ return true;
39
+ }
40
+ else if (rejectUnsupportedOperations) {
41
+ throw new SqlUnsupportedOperationError(sql, 'Result name is not a string.', ast);
42
+ }
43
+ else {
44
+ return false;
45
+ }
46
+ });
47
+ return {
48
+ ...sortValues({
49
+ csvFileHeaderOrder: csvHeaders,
50
+ sqlQueryHeaderOrder: columnNames,
51
+ from: {
52
+ csvFile: csvContents.filter((row, index) => rowIndexesToSelect.includes(index)),
53
+ },
54
+ unconsumedInterpolationValues: sql.unconsumedValues,
55
+ }),
56
+ numberOfRowsAffected: 0,
57
+ };
42
58
  },
43
59
  });
@@ -1,8 +1,7 @@
1
- import { assertWrap, check } from '@augment-vir/assert';
2
- import { awaitedBlockingMap } from '@augment-vir/common';
1
+ import { assertWrap } from '@augment-vir/assert';
3
2
  import { createCsvHeaderMaps, nameCsvTableFile, readCsvFile, readCsvHeaders, writeCsvFile, } from '../../csv/csv-file.js';
4
3
  import { CsvColumnDoesNotExistError } from '../../errors/csv.error.js';
5
- import { AstType } from '../../sql/ast.js';
4
+ import { getAstType } from '../../util/ast-node.js';
6
5
  import { defineAstHandler } from '../define-ast-handler.js';
7
6
  import { sortValues } from '../sort-values.js';
8
7
  import { findWhereMatches } from '../where-matcher.js';
@@ -14,48 +13,66 @@ import { findWhereMatches } from '../where-matcher.js';
14
13
  export const rowUpdateHandler = defineAstHandler({
15
14
  name: 'row-update',
16
15
  async handler({ ast, csvDirPath, sql }) {
17
- if (ast.type === AstType.Update) {
18
- const tableNames = ast.table.map((table) => table.table);
19
- const results = 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 sqlHeaders = returningRequirement?.columns.map((column) => column.expr.column) || [];
44
- const result = returningRequirement
45
- ? sortValues({
46
- csvFileHeaderOrder: csvHeaders,
47
- sqlQueryHeaderOrder: sqlHeaders,
48
- from: {
49
- csvFile: csvContents.filter((row, index) => rowIndexesToUpdate.includes(index)),
50
- },
51
- unconsumedInterpolationValues: sql.unconsumedValues,
52
- })
53
- : undefined;
54
- await writeCsvFile(tableFilePath, csvContents);
55
- return result;
56
- });
57
- return results.flat().filter(check.isTruthy);
16
+ if (ast.variant !== 'update') {
17
+ return undefined;
18
+ }
19
+ const tableName = getAstType(ast.into, 'identifier')?.name;
20
+ if (!tableName) {
21
+ throw new Error('No table name');
58
22
  }
59
- return undefined;
23
+ const { tableFilePath, sanitizedTableName } = nameCsvTableFile({
24
+ csvDirPath,
25
+ tableName,
26
+ });
27
+ const csvContents = await readCsvFile(tableFilePath);
28
+ const csvHeaders = await readCsvHeaders({
29
+ csvContents,
30
+ sanitizedTableName,
31
+ });
32
+ const csvHeaderIndexes = createCsvHeaderMaps(csvHeaders);
33
+ const rowIndexesToUpdate = findWhereMatches(ast.where, csvContents, tableFilePath);
34
+ const returningRequirement = ast.returning;
35
+ rowIndexesToUpdate.forEach((rowIndexToUpdate) => {
36
+ const row = assertWrap.isTruthy(csvContents[rowIndexToUpdate], `Invalid row index '${rowIndexToUpdate}'.`);
37
+ ast.set.forEach((set) => {
38
+ const columnName = set.target.name;
39
+ const headerIndex = csvHeaderIndexes.byName[columnName];
40
+ if (!headerIndex) {
41
+ throw new CsvColumnDoesNotExistError(sanitizedTableName, columnName);
42
+ }
43
+ const valueNode = getAstType(set.value, 'literal');
44
+ if (!valueNode) {
45
+ throw new Error(`Unexpected set type: ${set.value.type}`);
46
+ }
47
+ row[headerIndex] = valueNode.value;
48
+ });
49
+ });
50
+ const sqlHeaders = returningRequirement?.map((column) => {
51
+ const columnNode = getAstType(column, 'identifier');
52
+ if (columnNode) {
53
+ return columnNode.name;
54
+ }
55
+ else {
56
+ throw new Error(`Unexpected return column type: ${column.type}`);
57
+ }
58
+ }) || [];
59
+ const result = returningRequirement
60
+ ? sortValues({
61
+ csvFileHeaderOrder: csvHeaders,
62
+ sqlQueryHeaderOrder: sqlHeaders,
63
+ from: {
64
+ csvFile: csvContents.filter((row, index) => rowIndexesToUpdate.includes(index)),
65
+ },
66
+ unconsumedInterpolationValues: sql.unconsumedValues,
67
+ })
68
+ : {
69
+ columnNames: [],
70
+ values: [],
71
+ };
72
+ await writeCsvFile(tableFilePath, csvContents);
73
+ return {
74
+ ...result,
75
+ numberOfRowsAffected: rowIndexesToUpdate.length,
76
+ };
60
77
  },
61
78
  });