Comparing version 1.0.223 to 1.0.225
@@ -5,3 +5,5 @@ "use strict"; | ||
const sqlstring_1 = require("sqlstring"); | ||
const sqlstring_sqlite_1 = require("sqlstring-sqlite"); | ||
const pg_escape = require("pg-escape-browser"); | ||
const helpers_1 = require("./helpers"); | ||
/** | ||
@@ -12,9 +14,13 @@ * Small wrapper over sqlstring escape to prevent sqlstring from casting numbers into strings | ||
const orma_escape = (val, database_type) => { | ||
const escape_fn = database_type === 'mysql' | ||
? val => (0, sqlstring_1.escape)(val, true, '+00') | ||
: typeof val === 'object' | ||
? el => el | ||
: pg_escape.literal; | ||
return typeof val === 'number' ? val : escape_fn(val); | ||
const parse_functions = { | ||
mysql: val => (0, sqlstring_1.escape)(val, true, '+00'), | ||
sqlite: val => (0, sqlstring_sqlite_1.escape)(val, true, '+00'), | ||
postgres: pg_escape.literal, | ||
}; | ||
const escape_fn = parse_functions[database_type]; | ||
// guids could get in here, dont escape them. Note other object-like things such as | ||
// Dates and arrays should be parsed. | ||
const dont_parse = typeof val === 'number' || (0, helpers_1.is_simple_object)(val); | ||
return dont_parse ? val : escape_fn(val); | ||
}; | ||
exports.orma_escape = orma_escape; |
@@ -5,4 +5,7 @@ import * as sqlite3 from 'sqlite3'; | ||
export declare const close_sqlite_database: (db: sqlite3.Database) => Promise<void>; | ||
export declare const set_up_test_database: (orma_schema: OrmaSchema, hydration_mutation: Record<string, any>, directory_path: string) => Promise<sqlite3.Database>; | ||
/** | ||
* Call once before all tests run. Make sure the orma_schema entities have $database_type set to 'sqlite' | ||
*/ | ||
export declare const set_up_test_database: (orma_schema: OrmaSchema, hydration_data: Record<string, any>, directory_path: string) => Promise<sqlite3.Database>; | ||
export declare const tear_down_test_database: (test_database: sqlite3.Database | undefined, directory_path: string) => Promise<void>; | ||
export declare const reset_test_database: (test_database: sqlite3.Database | undefined, directory_path: string) => Promise<sqlite3.Database>; |
@@ -23,3 +23,6 @@ "use strict"; | ||
exports.close_sqlite_database = close_sqlite_database; | ||
const set_up_test_database = async (orma_schema, hydration_mutation, directory_path) => { | ||
/** | ||
* Call once before all tests run. Make sure the orma_schema entities have $database_type set to 'sqlite' | ||
*/ | ||
const set_up_test_database = async (orma_schema, hydration_data, directory_path) => { | ||
clear_database_files(directory_path); | ||
@@ -32,3 +35,3 @@ const db = await (0, exports.open_sqlite_database)(get_db_path(directory_path)); | ||
await (0, database_adapters_1.sqlite3_adapter)(db)(statements); | ||
await (0, mutate_1.orma_mutate)(hydration_mutation, (0, database_adapters_1.sqlite3_adapter)(db), orma_schema); | ||
await (0, mutate_1.orma_mutate)(Object.assign({ $operation: 'create' }, hydration_data), (0, database_adapters_1.sqlite3_adapter)(db), orma_schema); | ||
(0, fs_1.copyFileSync)(get_db_path(directory_path), get_checkpoint_path(directory_path)); | ||
@@ -35,0 +38,0 @@ return db; |
@@ -299,3 +299,3 @@ "use strict"; | ||
// and the constraint type (e.g. FOREIGN KEY) | ||
get_neighbour_field(obj, path, '$constraint') ? '' : `\`${arg}\``, $data_type: (arg, path, obj, database_type) => { | ||
get_neighbour_field(obj, path, '$constraint') ? '' : arg, $data_type: (arg, path, obj, database_type) => { | ||
var _a; | ||
@@ -319,9 +319,21 @@ const precision = get_neighbour_field(obj, path, '$precision'); | ||
return `${arg === null || arg === void 0 ? void 0 : arg.toUpperCase()}${data_type_args ? `(${data_type_args})` : ''}`; | ||
}, $unsigned: arg => (arg ? 'UNSIGNED' : ''), $precision: arg => '', $scale: arg => '', $enum_values: arg => ``, $not_null: arg => (arg ? 'NOT NULL' : ''), $default: arg => `DEFAULT ${arg}`, $auto_increment: (arg, path, obj, database_type) => arg && database_type !== 'sqlite' ? 'AUTO_INCREMENT' : '', | ||
}, $unsigned: arg => (arg ? 'UNSIGNED' : ''), $precision: arg => '', $scale: arg => '', $enum_values: arg => ``, $not_null: arg => (arg ? 'NOT NULL' : ''), $default: arg => `DEFAULT ${arg}`, $auto_increment: (arg, path, obj, database_type) => { | ||
if (!arg) { | ||
return ''; | ||
} | ||
// Sqlite needs the magic INTEGER PRIMARY KEY type to do auto incrementing, | ||
// so we parse $auto_incrementing as an inline PRIMARY KEY constraint | ||
return database_type === 'sqlite' ? 'PRIMARY KEY' : 'AUTO_INCREMENT'; | ||
}, | ||
// index | ||
$fields: args => `(${args.join(', ')})`, $invisible: arg => (arg ? `INVISIBLE` : ''), $comment: (arg, path, obj, database_type) => | ||
$fields: args => `(${args.join(', ')})`, $invisible: (arg, path, obj, database_type) => | ||
// sqlite doesnt support invisible indexes | ||
arg && database_type !== 'sqlite' ? `INVISIBLE` : '', $comment: (arg, path, obj, database_type) => | ||
// sqlite doesnt support the COMMENT keyword | ||
database_type === 'sqlite' ? '' : `COMMENT "${arg}"`, | ||
// constraint | ||
$references: arg => `REFERENCES ${arg}`, $on_delete: arg => `ON DELETE ${arg}`, $on_update: arg => `ON UPDATE ${arg}`, $restrict: arg => (arg ? `RESTRICT` : ''), $cascade: arg => (arg ? `CASCADE` : ''), $set_null: arg => (arg ? `SET NULL` : ''), $no_action: arg => (arg ? `NO ACTION` : '') }); | ||
$references: arg => `REFERENCES ${arg}`, $on_delete: arg => `ON DELETE ${arg}`, $on_update: arg => `ON UPDATE ${arg}`, $restrict: arg => (arg ? `RESTRICT` : ''), $cascade: arg => (arg ? `CASCADE` : ''), $set_null: arg => (arg ? `SET NULL` : ''), $no_action: arg => (arg ? `NO ACTION` : ''), | ||
// this is just here for sqlite compatibility. It is purposefully not included in the types | ||
// because no one should be using such a bad and inconsistent syntax | ||
$create_index: arg => `CREATE INDEX \`${arg}\``, $on: arg => `ON ${arg}` }); | ||
const is_sql_null = val => { | ||
@@ -328,0 +340,0 @@ var _a; |
@@ -421,3 +421,3 @@ "use strict"; | ||
ALTER TABLE my_table ( | ||
ADD CONSTRAINT uq_ind UNIQUE (label) | ||
ADD CONSTRAINT \`uq_ind\` UNIQUE (label) | ||
)`); | ||
@@ -444,2 +444,15 @@ (0, chai_1.expect)((0, sql_formatter_1.format)((0, json_sql_1.json_to_sql)(json))).to.equal(goal); | ||
}); | ||
(0, mocha_1.test)('handles CREATE INDEX syntax', () => { | ||
const json = { | ||
$create_index: 'my_index', | ||
$on: { | ||
$entity: 'my_table', | ||
$fields: ['field1', 'field2'], | ||
}, | ||
}; | ||
const goal = (0, sql_formatter_1.format)(` | ||
CREATE INDEX \`my_index\` ON my_table (field1, field2) | ||
`); | ||
(0, chai_1.expect)((0, sql_formatter_1.format)((0, json_sql_1.json_to_sql)(json))).to.equal(goal); | ||
}); | ||
(0, mocha_1.test)('handles primary key', () => { | ||
@@ -459,3 +472,3 @@ const json = { | ||
ALTER TABLE my_table ( | ||
ADD CONSTRAINT primary PRIMARY KEY (id) | ||
ADD CONSTRAINT \`primary\` PRIMARY KEY (id) | ||
)`); | ||
@@ -493,3 +506,3 @@ (0, chai_1.expect)((0, sql_formatter_1.format)((0, json_sql_1.json_to_sql)(json))).to.equal(goal); | ||
ADD FOREIGN KEY (parent_id) REFERENCES parents (id), | ||
ADD CONSTRAINT my_foreign_key FOREIGN KEY (parent_id) REFERENCES parents (id) | ||
ADD CONSTRAINT \`my_foreign_key\` FOREIGN KEY (parent_id) REFERENCES parents (id) | ||
ON DELETE CASCADE ON UPDATE RESTRICT, | ||
@@ -496,0 +509,0 @@ ADD FOREIGN KEY (parent_id) REFERENCES parents (id) |
@@ -151,3 +151,3 @@ "use strict"; | ||
const field_schema = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ $data_type: data_type.toLowerCase() }, ((enum_values === null || enum_values === void 0 ? void 0 : enum_values.length) && { $enum_values: enum_values })), (precision && { $precision: precision })), (numeric_scale && { $scale: numeric_scale })), ((column_type === null || column_type === void 0 ? void 0 : column_type.match(/unsigned/)) && { $unsigned: true })), (extra === 'auto_increment' && { $auto_increment: true })), ((identity_generation || column_default) && { | ||
$default: identity_generation || column_default, | ||
$default: identity_generation || parse_column_default(column_default), | ||
})), (is_nullable === 'NO' && { $not_null: true })), (column_comment && { $comment: column_comment })); | ||
@@ -157,2 +157,21 @@ return field_schema; | ||
exports.generate_field_schema = generate_field_schema; | ||
const parse_column_default = (value) => { | ||
// Mysql doesnt properly put quotes on their default values, so we need to string parse | ||
// and handle all the cases. In the Orma schema there should be no ambiguity, even though | ||
// the mysql information schema does a bad job here | ||
var _a, _b; | ||
if (!value) { | ||
return undefined; | ||
} | ||
if (((_a = value === null || value === void 0 ? void 0 : value.toLowerCase) === null || _a === void 0 ? void 0 : _a.call(value)) === 'current_timestamp') { | ||
return value; | ||
} | ||
if (((_b = value === null || value === void 0 ? void 0 : value.toLowerCase) === null || _b === void 0 ? void 0 : _b.call(value)) === 'null') { | ||
return null; | ||
} | ||
if (!isNaN(Number(value))) { | ||
return Number(value); | ||
} | ||
return `'${value}'`; | ||
}; | ||
const generate_primary_key_schema = (mysql_columns) => { | ||
@@ -159,0 +178,0 @@ var _a; |
import { RegularCreateStatement } from '../types/schema/schema_ast_types'; | ||
import { OrmaSchema } from '../types/schema/schema_types'; | ||
export declare const get_schema_diff: (original_schema: OrmaSchema, final_schema: OrmaSchema) => RegularCreateStatement[]; | ||
export declare const get_schema_diff: (original_schema: OrmaSchema, final_schema: OrmaSchema) => (RegularCreateStatement | { | ||
$create_index: string | undefined; | ||
$on: { | ||
$entity: string; | ||
$fields: readonly string[]; | ||
}; | ||
})[]; |
@@ -13,3 +13,4 @@ "use strict"; | ||
const sorted_create_statements = get_sorted_create_table_statements(final_schema, create_entity_statements); | ||
return sorted_create_statements; | ||
const sqlite_create_index_statements = entities_to_create.flatMap(entity => get_create_index_statements_for_sqlite(entity, final_schema.$entities[entity])); | ||
return [...sorted_create_statements, ...sqlite_create_index_statements]; | ||
}; | ||
@@ -19,7 +20,23 @@ exports.get_schema_diff = get_schema_diff; | ||
var _a, _b, _c, _d, _e, _f, _g; | ||
const fields = (_a = Object.keys(entity_schema.$fields).map(field_name => (Object.assign({ $name: field_name }, entity_schema.$fields[field_name])))) !== null && _a !== void 0 ? _a : []; | ||
const database_type = entity_schema.$database_type; | ||
const fields = (_a = Object.keys(entity_schema.$fields).map(field_name => { | ||
const field_schema = entity_schema.$fields[field_name]; | ||
// For sqlite to do auto incrementing, we need the magic INTEGER PRIMARY KEY | ||
// type. Having UNSIGNED or a precision like INTEGER(10) will cause it to | ||
// not auto increment. | ||
if (database_type === 'sqlite' && field_schema.$auto_increment) { | ||
return { | ||
$name: field_name, | ||
$data_type: 'int', | ||
$auto_increment: true | ||
}; | ||
} | ||
else { | ||
return Object.assign({ $name: field_name }, field_schema); | ||
} | ||
})) !== null && _a !== void 0 ? _a : []; | ||
const primary_key = Object.assign({ $constraint: 'primary_key' }, entity_schema.$primary_key); | ||
const indexes = (_c = (_b = entity_schema === null || entity_schema === void 0 ? void 0 : entity_schema.$indexes) === null || _b === void 0 ? void 0 : _b.map(el => (Object.assign(Object.assign({}, (el.$index ? { $index: el.$index } : { $index: true })), el)))) !== null && _c !== void 0 ? _c : []; | ||
const unique_keys = (_e = (_d = entity_schema === null || entity_schema === void 0 ? void 0 : entity_schema.$unique_keys) === null || _d === void 0 ? void 0 : _d.map(el => (Object.assign({ $constraint: 'unique_key' }, el)))) !== null && _e !== void 0 ? _e : []; | ||
const foreign_keys = (_g = (_f = entity_schema === null || entity_schema === void 0 ? void 0 : entity_schema.$foreign_keys) === null || _f === void 0 ? void 0 : _f.map(el => (Object.assign({ $constraint: 'foreign_key' }, el)))) !== null && _g !== void 0 ? _g : []; | ||
const unique_keys = (_c = (_b = entity_schema === null || entity_schema === void 0 ? void 0 : entity_schema.$unique_keys) === null || _b === void 0 ? void 0 : _b.map(el => (Object.assign({ $constraint: 'unique_key' }, el)))) !== null && _c !== void 0 ? _c : []; | ||
const foreign_keys = (_e = (_d = entity_schema === null || entity_schema === void 0 ? void 0 : entity_schema.$foreign_keys) === null || _d === void 0 ? void 0 : _d.map(el => (Object.assign({ $constraint: 'foreign_key' }, el)))) !== null && _e !== void 0 ? _e : []; | ||
const indexes = (_g = (_f = entity_schema === null || entity_schema === void 0 ? void 0 : entity_schema.$indexes) === null || _f === void 0 ? void 0 : _f.map(el => (Object.assign(Object.assign({}, (el.$index ? { $index: el.$index } : { $index: true })), el)))) !== null && _g !== void 0 ? _g : []; | ||
return { | ||
@@ -30,9 +47,29 @@ $create_table: entity_name, | ||
...fields, | ||
primary_key, | ||
...unique_keys, | ||
...foreign_keys, | ||
...indexes, | ||
// primary key and indexes are special cases in sqlite that are handled separately | ||
...(database_type !== 'sqlite' ? [primary_key, ...indexes] : []), | ||
], | ||
}; | ||
}; | ||
/** | ||
* Unlike literally everything else like table options, fields, constraints, foreign keys etc, | ||
* SQLite insists that indexes are created using a completely separate CREATE INDEX syntax. | ||
* So this needs to be done separately to cover for SQLite's poor design choices. | ||
*/ | ||
const get_create_index_statements_for_sqlite = (entity_name, entity_schema) => { | ||
var _a, _b; | ||
if (entity_schema.$database_type !== 'sqlite') { | ||
// indexes handled in the create statement for non-sqlite | ||
return []; | ||
} | ||
const indexes = (_b = (_a = entity_schema === null || entity_schema === void 0 ? void 0 : entity_schema.$indexes) === null || _a === void 0 ? void 0 : _a.map(el => (Object.assign(Object.assign({}, (el.$index ? { $index: el.$index } : { $index: true })), el)))) !== null && _b !== void 0 ? _b : []; | ||
return indexes.map(index => ({ | ||
$create_index: index.$name, | ||
$on: { | ||
$entity: entity_name, | ||
$fields: index.$fields, | ||
}, | ||
})); | ||
}; | ||
const get_sorted_create_table_statements = (final_schema, create_statements) => { | ||
@@ -49,4 +86,2 @@ // we make a fake mutation, which allows us to use the mutation planner to order our statements. | ||
acc[edge.from_field] = 1; | ||
// keep track of the statement to convert the sorted mutation pieces back to statements | ||
acc.$_statement_index = i; | ||
return acc; | ||
@@ -56,3 +91,5 @@ }, {}); | ||
path: [entity, 0], | ||
record: Object.assign({ $operation: 'create' }, edge_fields_obj), | ||
record: Object.assign({ $operation: 'create', | ||
// keep track of the statement to convert the sorted mutation pieces back to statements | ||
$_statement_index: i }, edge_fields_obj), | ||
}; | ||
@@ -59,0 +96,0 @@ }); |
@@ -6,3 +6,3 @@ import { OrmaMutation } from '../types/mutation/mutation_types'; | ||
readonly users: { | ||
readonly $database_type: "mysql"; | ||
readonly $database_type: "sqlite"; | ||
readonly $fields: { | ||
@@ -54,3 +54,3 @@ readonly id: { | ||
readonly posts: { | ||
readonly $database_type: "mysql"; | ||
readonly $database_type: "sqlite"; | ||
readonly $fields: { | ||
@@ -92,3 +92,3 @@ readonly id: { | ||
readonly likes: { | ||
readonly $database_type: "mysql"; | ||
readonly $database_type: "sqlite"; | ||
readonly $fields: { | ||
@@ -131,3 +131,3 @@ readonly id: { | ||
readonly comments: { | ||
readonly $database_type: "mysql"; | ||
readonly $database_type: "sqlite"; | ||
readonly $fields: { | ||
@@ -156,3 +156,3 @@ readonly id: { | ||
readonly addresses: { | ||
readonly $database_type: "mysql"; | ||
readonly $database_type: "sqlite"; | ||
readonly $fields: { | ||
@@ -190,3 +190,3 @@ readonly id: { | ||
readonly tax_codes: { | ||
readonly $database_type: "mysql"; | ||
readonly $database_type: "sqlite"; | ||
readonly $fields: { | ||
@@ -212,3 +212,3 @@ readonly id: { | ||
readonly categories: { | ||
readonly $database_type: "mysql"; | ||
readonly $database_type: "sqlite"; | ||
readonly $fields: { | ||
@@ -247,3 +247,3 @@ readonly id: { | ||
readonly post_has_categories: { | ||
readonly $database_type: "mysql"; | ||
readonly $database_type: "sqlite"; | ||
readonly $fields: { | ||
@@ -250,0 +250,0 @@ readonly post_id: { |
@@ -7,3 +7,3 @@ "use strict"; | ||
users: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -61,3 +61,3 @@ id: { | ||
posts: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -103,3 +103,3 @@ id: { | ||
likes: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -147,3 +147,3 @@ id: { | ||
comments: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -174,3 +174,3 @@ id: { | ||
addresses: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -212,3 +212,3 @@ id: { | ||
tax_codes: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -236,3 +236,3 @@ id: { | ||
categories: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -274,3 +274,3 @@ id: { | ||
post_has_categories: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -277,0 +277,0 @@ post_id: { |
{ | ||
"name": "orma", | ||
"version": "1.0.223", | ||
"version": "1.0.225", | ||
"description": "A declarative relational syncronous orm", | ||
@@ -80,4 +80,5 @@ "main": "build/index.js", | ||
"pg-escape-browser": "^0.1.0", | ||
"sqlstring": "^2.3.3" | ||
"sqlstring": "^2.3.3", | ||
"sqlstring-sqlite": "^0.1.1" | ||
} | ||
} |
@@ -1,4 +0,6 @@ | ||
import { escape } from 'sqlstring' | ||
import { escape as escape_mysql } from 'sqlstring' | ||
import { escape as escape_sqlite } from 'sqlstring-sqlite' | ||
import * as pg_escape from 'pg-escape-browser' | ||
import { SupportedDatabases } from '../types/schema/schema_types' | ||
import { is_simple_object } from './helpers' | ||
@@ -10,10 +12,16 @@ /** | ||
export const orma_escape = (val: any, database_type: SupportedDatabases) => { | ||
const escape_fn = | ||
database_type === 'mysql' | ||
? val => escape(val, true, '+00') | ||
: typeof val === 'object' | ||
? el => el | ||
: pg_escape.literal | ||
const parse_functions = { | ||
mysql: val => escape_mysql(val, true, '+00'), | ||
sqlite: val => escape_sqlite(val, true, '+00'), | ||
postgres: pg_escape.literal, | ||
} | ||
return typeof val === 'number' ? val : escape_fn(val) | ||
const escape_fn = parse_functions[database_type] | ||
// guids could get in here, dont escape them. Note other object-like things such as | ||
// Dates and arrays should be parsed. | ||
const dont_parse = | ||
typeof val === 'number' || is_simple_object(val) | ||
return dont_parse ? val : escape_fn(val) | ||
} |
@@ -30,5 +30,8 @@ import * as sqlite3 from 'sqlite3' | ||
/** | ||
* Call once before all tests run. Make sure the orma_schema entities have $database_type set to 'sqlite' | ||
*/ | ||
export const set_up_test_database = async ( | ||
orma_schema: OrmaSchema, | ||
hydration_mutation: Record<string, any>, | ||
hydration_data: Record<string, any>, | ||
directory_path: string | ||
@@ -44,3 +47,7 @@ ) => { | ||
await sqlite3_adapter(db)(statements) | ||
await orma_mutate(hydration_mutation, sqlite3_adapter(db), orma_schema) | ||
await orma_mutate( | ||
{ $operation: 'create', ...hydration_data }, | ||
sqlite3_adapter(db), | ||
orma_schema | ||
) | ||
copyFileSync( | ||
@@ -47,0 +54,0 @@ get_db_path(directory_path), |
@@ -473,3 +473,3 @@ import { expect } from 'chai' | ||
ALTER TABLE my_table ( | ||
ADD CONSTRAINT uq_ind UNIQUE (label) | ||
ADD CONSTRAINT \`uq_ind\` UNIQUE (label) | ||
)`) | ||
@@ -499,2 +499,17 @@ | ||
}) | ||
test('handles CREATE INDEX syntax', () => { | ||
const json = { | ||
$create_index: 'my_index', | ||
$on: { | ||
$entity: 'my_table', | ||
$fields: ['field1', 'field2'], | ||
}, | ||
} | ||
const goal = format(` | ||
CREATE INDEX \`my_index\` ON my_table (field1, field2) | ||
`) | ||
expect(format(json_to_sql(json))).to.equal(goal) | ||
}) | ||
test('handles primary key', () => { | ||
@@ -515,3 +530,3 @@ const json: AlterStatement = { | ||
ALTER TABLE my_table ( | ||
ADD CONSTRAINT primary PRIMARY KEY (id) | ||
ADD CONSTRAINT \`primary\` PRIMARY KEY (id) | ||
)`) | ||
@@ -561,3 +576,3 @@ | ||
ADD FOREIGN KEY (parent_id) REFERENCES parents (id), | ||
ADD CONSTRAINT my_foreign_key FOREIGN KEY (parent_id) REFERENCES parents (id) | ||
ADD CONSTRAINT \`my_foreign_key\` FOREIGN KEY (parent_id) REFERENCES parents (id) | ||
ON DELETE CASCADE ON UPDATE RESTRICT, | ||
@@ -564,0 +579,0 @@ ADD FOREIGN KEY (parent_id) REFERENCES parents (id) |
@@ -449,3 +449,3 @@ /** | ||
// and the constraint type (e.g. FOREIGN KEY) | ||
get_neighbour_field(obj, path, '$constraint') ? '' : `\`${arg}\``, | ||
get_neighbour_field(obj, path, '$constraint') ? '' : arg, | ||
$data_type: (arg, path, obj, database_type) => { | ||
@@ -483,7 +483,16 @@ const precision = get_neighbour_field(obj, path, '$precision') | ||
$default: arg => `DEFAULT ${arg}`, | ||
$auto_increment: (arg, path, obj, database_type) => | ||
arg && database_type !== 'sqlite' ? 'AUTO_INCREMENT' : '', | ||
$auto_increment: (arg, path, obj, database_type) => { | ||
if (!arg) { | ||
return '' | ||
} | ||
// Sqlite needs the magic INTEGER PRIMARY KEY type to do auto incrementing, | ||
// so we parse $auto_incrementing as an inline PRIMARY KEY constraint | ||
return database_type === 'sqlite' ? 'PRIMARY KEY' : 'AUTO_INCREMENT' | ||
}, | ||
// index | ||
$fields: args => `(${args.join(', ')})`, | ||
$invisible: arg => (arg ? `INVISIBLE` : ''), | ||
$invisible: (arg, path, obj, database_type: SupportedDatabases) => | ||
// sqlite doesnt support invisible indexes | ||
arg && database_type !== 'sqlite' ? `INVISIBLE` : '', | ||
$comment: (arg, path, obj, database_type: SupportedDatabases) => | ||
@@ -500,2 +509,6 @@ // sqlite doesnt support the COMMENT keyword | ||
$no_action: arg => (arg ? `NO ACTION` : ''), | ||
// this is just here for sqlite compatibility. It is purposefully not included in the types | ||
// because no one should be using such a bad and inconsistent syntax | ||
$create_index: arg => `CREATE INDEX \`${arg}\``, | ||
$on: arg => `ON ${arg}`, | ||
} | ||
@@ -502,0 +515,0 @@ |
@@ -259,3 +259,4 @@ /** | ||
...((identity_generation || column_default) && { | ||
$default: identity_generation || column_default, | ||
$default: | ||
identity_generation || parse_column_default(column_default), | ||
}), | ||
@@ -269,2 +270,26 @@ ...(is_nullable === 'NO' && { $not_null: true }), | ||
const parse_column_default = (value: any) => { | ||
// Mysql doesnt properly put quotes on their default values, so we need to string parse | ||
// and handle all the cases. In the Orma schema there should be no ambiguity, even though | ||
// the mysql information schema does a bad job here | ||
if (!value) { | ||
return undefined | ||
} | ||
if (value?.toLowerCase?.() === 'current_timestamp') { | ||
return value | ||
} | ||
if (value?.toLowerCase?.() === 'null') { | ||
return null | ||
} | ||
if (!isNaN(Number(value))) { | ||
return Number(value) | ||
} | ||
return `'${value}'` | ||
} | ||
const generate_primary_key_schema = (mysql_columns: MysqlColumn[]) => { | ||
@@ -271,0 +296,0 @@ const primary_key_columns = mysql_columns.filter( |
@@ -1,2 +0,2 @@ | ||
import { get_difference } from '../helpers/helpers' | ||
import { array_equals, get_difference } from '../helpers/helpers' | ||
import { get_all_edges } from '../helpers/schema_helpers' | ||
@@ -32,3 +32,10 @@ import { | ||
return sorted_create_statements | ||
const sqlite_create_index_statements = entities_to_create.flatMap(entity => | ||
get_create_index_statements_for_sqlite( | ||
entity, | ||
final_schema.$entities[entity] | ||
) | ||
) | ||
return [...sorted_create_statements, ...sqlite_create_index_statements] | ||
} | ||
@@ -40,7 +47,23 @@ | ||
) => { | ||
const database_type = entity_schema.$database_type | ||
const fields: FieldDefinition[] = | ||
Object.keys(entity_schema.$fields).map(field_name => ({ | ||
$name: field_name, | ||
...entity_schema.$fields[field_name], | ||
})) ?? [] | ||
Object.keys(entity_schema.$fields).map(field_name => { | ||
const field_schema = entity_schema.$fields[field_name] | ||
// For sqlite to do auto incrementing, we need the magic INTEGER PRIMARY KEY | ||
// type. Having UNSIGNED or a precision like INTEGER(10) will cause it to | ||
// not auto increment. | ||
if (database_type === 'sqlite' && field_schema.$auto_increment) { | ||
return { | ||
$name: field_name, | ||
$data_type: 'int', | ||
$auto_increment: true | ||
} | ||
} else { | ||
return { | ||
$name: field_name, | ||
...field_schema, | ||
} | ||
} | ||
}) ?? [] | ||
@@ -52,8 +75,2 @@ const primary_key: ConstraintDefinition = { | ||
const indexes: IndexDefinition[] = | ||
entity_schema?.$indexes?.map(el => ({ | ||
...(el.$index ? { $index: el.$index } : { $index: true }), | ||
...el, | ||
})) ?? [] | ||
const unique_keys: ConstraintDefinition[] = | ||
@@ -71,2 +88,8 @@ entity_schema?.$unique_keys?.map(el => ({ | ||
const indexes: IndexDefinition[] = | ||
entity_schema?.$indexes?.map(el => ({ | ||
...(el.$index ? { $index: el.$index } : { $index: true }), | ||
...el, | ||
})) ?? [] | ||
return { | ||
@@ -77,6 +100,6 @@ $create_table: entity_name, | ||
...fields, | ||
primary_key, | ||
...unique_keys, | ||
...foreign_keys, | ||
...indexes, | ||
// primary key and indexes are special cases in sqlite that are handled separately | ||
...(database_type !== 'sqlite' ? [primary_key, ...indexes] : []), | ||
], | ||
@@ -86,2 +109,31 @@ } | ||
/** | ||
* Unlike literally everything else like table options, fields, constraints, foreign keys etc, | ||
* SQLite insists that indexes are created using a completely separate CREATE INDEX syntax. | ||
* So this needs to be done separately to cover for SQLite's poor design choices. | ||
*/ | ||
const get_create_index_statements_for_sqlite = ( | ||
entity_name: string, | ||
entity_schema: OrmaSchema['$entities'][string] | ||
) => { | ||
if (entity_schema.$database_type !== 'sqlite') { | ||
// indexes handled in the create statement for non-sqlite | ||
return [] | ||
} | ||
const indexes: IndexDefinition[] = | ||
entity_schema?.$indexes?.map(el => ({ | ||
...(el.$index ? { $index: el.$index } : { $index: true }), | ||
...el, | ||
})) ?? [] | ||
return indexes.map(index => ({ | ||
$create_index: index.$name, | ||
$on: { | ||
$entity: entity_name, | ||
$fields: index.$fields, | ||
}, | ||
})) | ||
} | ||
const get_sorted_create_table_statements = ( | ||
@@ -105,4 +157,2 @@ final_schema: OrmaSchema, | ||
acc[edge.from_field] = 1 | ||
// keep track of the statement to convert the sorted mutation pieces back to statements | ||
acc.$_statement_index = i | ||
return acc | ||
@@ -115,2 +165,4 @@ }, {} as Record<string, any>) | ||
$operation: 'create', | ||
// keep track of the statement to convert the sorted mutation pieces back to statements | ||
$_statement_index: i, | ||
...edge_fields_obj, | ||
@@ -117,0 +169,0 @@ }, |
@@ -8,3 +8,3 @@ import { OrmaMutation } from '../types/mutation/mutation_types' | ||
users: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -62,3 +62,3 @@ id: { | ||
posts: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -104,3 +104,3 @@ id: { | ||
likes: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -148,3 +148,3 @@ id: { | ||
comments: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -175,3 +175,3 @@ id: { | ||
addresses: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -213,3 +213,3 @@ id: { | ||
tax_codes: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -237,3 +237,3 @@ id: { | ||
categories: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -275,3 +275,3 @@ id: { | ||
post_has_categories: { | ||
$database_type: 'mysql', | ||
$database_type: 'sqlite', | ||
$fields: { | ||
@@ -278,0 +278,0 @@ post_id: { |
import { mysql_to_typescript_types } from '../../schema/introspector' | ||
/* | ||
{ | ||
$create_index: 'my_index', | ||
$on: { | ||
$entity: 'table' | ||
$fields: ['f1', 'f2'] | ||
} | ||
} | ||
*/ | ||
export type CreateStatement = RegularCreateStatement | CreateLikeStatement | ||
@@ -4,0 +16,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1592961
38836
4
+ Addedsqlstring-sqlite@^0.1.1
+ Addedsqlstring-sqlite@0.1.1(transitive)