Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SQLite support #2562

Merged
merged 12 commits into from
Jan 31, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ module.exports = {

// Search in columns with text using index.
switch (<%= globalID %>.client) {
case 'mysql':
qb.orWhereRaw(`MATCH(${searchText.join(',')}) AGAINST(? IN BOOLEAN MODE)`, `*${query}*`);
break;
case 'pg': {
const searchQuery = searchText.map(attribute =>
_.toLower(attribute) === attribute
Expand All @@ -220,9 +223,6 @@ module.exports = {
qb.orWhereRaw(`${searchQuery.join(' || ')} @@ to_tsquery(?)`, query);
break;
}
default:
qb.orWhereRaw(`MATCH(${searchText.join(',')}) AGAINST(? IN BOOLEAN MODE)`, `*${query}*`);
break;
}

if (filters.sort) {
Expand Down
64 changes: 47 additions & 17 deletions packages/strapi-generate-new/lib/before.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ module.exports = (scope, cb) => {

const connectionValidation = () => {
const databaseChoices = [
{
name: 'SQLite',
value: {
database: 'sqlite',
connector: 'strapi-hook-bookshelf',
module: 'sqlite3'
}
},
{
name: 'MongoDB',
value: {
Expand All @@ -78,19 +86,19 @@ module.exports = (scope, cb) => {
}
},
{
name: 'Postgres',
name: 'MySQL',
value: {
database: 'postgres',
database: 'mysql',
connector: 'strapi-hook-bookshelf',
module: 'pg'
module: 'mysql'
}
},
{
name: 'MySQL',
name: 'Postgres',
value: {
database: 'mysql',
database: 'postgres',
connector: 'strapi-hook-bookshelf',
module: 'mysql'
module: 'pg'
}
}
];
Expand Down Expand Up @@ -130,34 +138,37 @@ module.exports = (scope, cb) => {

const asyncFn = [
new Promise(resolve => {
const isMongo = scope.client.database === 'mongo';
const isSQLite = scope.database.settings.client === 'sqlite';

inquirer
.prompt([
{
when: !hasDatabaseConfig,
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'database',
message: 'Database name:',
default: _.get(scope.database, 'database', scope.name)
},
{
when: !hasDatabaseConfig,
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'host',
message: 'Host:',
default: _.get(scope.database, 'host', '127.0.0.1')
},
{
when: !hasDatabaseConfig && scope.client.database === 'mongo',
when: !hasDatabaseConfig && isMongo,
type: 'boolean',
name: 'srv',
message: '+srv connection:',
default: _.get(scope.database, 'srv', false)
},
{
when: !hasDatabaseConfig,
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'port',
message: `Port${scope.client.database === 'mongo' ? ' (It will be ignored if you enable +srv)' : ''}:`,
message: `Port${isMongo ? ' (It will be ignored if you enable +srv)' : ''}:`,
default: (answers) => { // eslint-disable-line no-unused-vars
if (_.get(scope.database, 'port')) {
return scope.database.port;
Expand All @@ -173,33 +184,40 @@ module.exports = (scope, cb) => {
}
},
{
when: !hasDatabaseConfig,
when: !hasDatabaseConfig && !isSQLite,
type: 'input',
name: 'username',
message: 'Username:',
default: _.get(scope.database, 'username', undefined)
},
{
when: !hasDatabaseConfig,
when: !hasDatabaseConfig && !isSQLite,
type: 'password',
name: 'password',
message: 'Password:',
mask: '*',
default: _.get(scope.database, 'password', undefined)
},
{
when: !hasDatabaseConfig && scope.client.database === 'mongo',
when: !hasDatabaseConfig && isMongo,
type: 'input',
name: 'authenticationDatabase',
message: 'Authentication database (Maybe "admin" or blank):',
default: _.get(scope.database, 'authenticationDatabase', undefined)
},
{
when: !hasDatabaseConfig,
when: !hasDatabaseConfig && !isSQLite,
type: 'boolean',
name: 'ssl',
message: 'Enable SSL connection:',
default: _.get(scope.database, 'ssl', false)
},
{
when: !hasDatabaseConfig && isSQLite,
type: 'input',
name: 'filename',
message: 'Filename:',
default: () => '.tmp/data.db'
}
])
.then(answers => {
Expand All @@ -212,15 +230,27 @@ module.exports = (scope, cb) => {
scope.database.settings.database = answers.database;
scope.database.settings.username = answers.username;
scope.database.settings.password = answers.password;
if (answers.filename) {
scope.database.settings.filename = answers.filename;
}
if (answers.srv) {
scope.database.settings.srv = _.toString(answers.srv) === 'true';
}
if (answers.authenticationDatabase) {
scope.database.options.authenticationDatabase = answers.authenticationDatabase;
}
if (scope.client.database === 'mongo') {

// SQLite requirements.
if (isSQLite) {
// Necessary for SQLite configuration (https://knexjs.org/#Builder-insert).
scope.database.options = {
useNullAsDefault: true
};
}

if (answers.ssl && scope.client.database === 'mongo') {
scope.database.options.ssl = _.toString(answers.ssl) === 'true';
} else {
} else if (answers.ssl) {
scope.database.settings.ssl = _.toString(answers.ssl) === 'true';
}

Expand Down
53 changes: 34 additions & 19 deletions packages/strapi-hook-bookshelf/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ module.exports = function(strapi) {
try {
// External function to map key that has been updated with `columnName`
const mapper = (params = {}) => {
if (definition.client === 'mysql') {
if (definition.client === 'mysql' || definition.client === 'sqlite3') {
Object.keys(params).map((key) => {
const attr = definition.attributes[key] || {};

Expand Down Expand Up @@ -305,7 +305,7 @@ module.exports = function(strapi) {
});

// Convert to JSON format stringify json for mysql database
if (definition.client === 'mysql') {
if (definition.client === 'mysql' || definition.client === 'sqlite3') {
const events = [{
name: 'saved',
target: 'afterSave'
Expand Down Expand Up @@ -411,7 +411,7 @@ module.exports = function(strapi) {
type = definition.client === 'pg' ? 'uuid' : 'varchar(36)';
break;
case 'text':
type = definition.client === 'pg' ? type = 'text' : 'longtext';
type = definition.client === 'pg' ? 'text' : 'longtext';
break;
case 'json':
type = definition.client === 'pg' ? 'jsonb' : 'longtext';
Expand Down Expand Up @@ -441,7 +441,17 @@ module.exports = function(strapi) {
type = definition.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP';
break;
case 'timestampUpdate':
type = definition.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
switch(definition.client) {
case 'pg':
type = 'timestamp with time zone';
break;
case 'sqlite3':
type = 'timestamp DEFAULT CURRENT_TIMESTAMP';
break;
default:
type = 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
break;
}
break;
case 'boolean':
type = 'boolean';
Expand Down Expand Up @@ -472,19 +482,28 @@ module.exports = function(strapi) {
try {
const connection = strapi.config.connections[definition.connection];
let columns = Object.keys(attributes).filter(attribute => ['string', 'text'].includes(attributes[attribute].type));
let indexes;
Copy link
Member

Choose a reason for hiding this comment

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

Why? It seems that the variable isn't used anywhere. We should keep it in the scope of the if condition?


if (!columns.length) {
// No text columns founds, exit from creating Fulltext Index
return;
}

switch (connection.settings.client) {
case 'pg': {
case 'mysql':
columns = columns
.map(attribute => `\`${attribute}\``)
.join(',');

// Create fulltext indexes for every column.
await ORM.knex.raw(`CREATE FULLTEXT INDEX SEARCH_${_.toUpper(_.snakeCase(table))} ON \`${table}\` (${columns})`);
break;
case 'pg':
// Enable extension to allow GIN indexes.
await ORM.knex.raw('CREATE EXTENSION IF NOT EXISTS pg_trgm');

// Create GIN indexes for every column.
const indexes = columns
indexes = columns
.map(column => {
const indexName = `${_.snakeCase(table)}_${column}`;
const attribute = _.toLower(column) === column
Expand All @@ -496,16 +515,6 @@ module.exports = function(strapi) {

await Promise.all(indexes);
break;
}

default:
columns = columns
.map(attribute => `\`${attribute}\``)
.join(',');

// Create fulltext indexes for every column.
await ORM.knex.raw(`CREATE FULLTEXT INDEX SEARCH_${_.toUpper(_.snakeCase(table))} ON \`${table}\` (${columns})`);
break;
}
} catch (e) {
// Handle duplicate errors.
Expand Down Expand Up @@ -537,7 +546,13 @@ module.exports = function(strapi) {


if (!tableExist) {
let idAttributeBuilder = [`id ${definition.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY`];
const defaultAttributeDifinitions = {
mysql: [`id INT AUTO_INCREMENT NOT NULL PRIMARY KEY`],
pg: [`id SERIAL NOT NULL PRIMARY KEY`],
sqlite3: ['id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL']
};

let idAttributeBuilder = defaultAttributeDifinitions[definition.client];
if (definition.primaryKeyType === 'uuid' && definition.client === 'pg') {
idAttributeBuilder = ['id uuid NOT NULL DEFAULT uuid_generate_v4() NOT NULL PRIMARY KEY'];
} else if (definition.primaryKeyType !== 'integer') {
Expand Down Expand Up @@ -608,11 +623,11 @@ module.exports = function(strapi) {
const type = getType(attributes[attribute], attribute);

if (type) {
const changeType = definition.client === 'pg'
const changeType = definition.client === 'pg' || definition.client === 'sqlite3'
? `ALTER COLUMN ${quote}${attribute}${quote} TYPE ${type} USING ${quote}${attribute}${quote}::${type}`
: `CHANGE ${quote}${attribute}${quote} ${quote}${attribute}${quote} ${type} `;

const changeRequired = definition.client === 'pg'
const changeRequired = definition.client === 'pg' || definition.client === 'sqlite3'
? `ALTER COLUMN ${quote}${attribute}${quote} ${attributes[attribute].required ? 'SET' : 'DROP'} NOT NULL`
: `CHANGE ${quote}${attribute}${quote} ${quote}${attribute}${quote} ${type} ${attributes[attribute].required ? 'NOT' : ''} NULL`;
await ORM.knex.raw(`ALTER TABLE ${quote}${table}${quote} ${changeType}`);
Expand Down
18 changes: 15 additions & 3 deletions packages/strapi-hook-bookshelf/lib/utils/connectivity.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
'use strict';

// Core
// Node.js core.
const path = require('path');

// Public node modules
const inquirer = require('inquirer');
const rimraf = require('rimraf');

module.exports = (scope, success, error) => {
if (scope.client.database === 'sqlite') {
return success();
}

let knex;

try {
Expand All @@ -18,15 +22,23 @@ module.exports = (scope, success, error) => {
knex = require(path.resolve(scope.tmpPath, 'node_modules', 'knex'));
}

// eslint-disable-next-line import/no-unresolved
knex = knex({
client: scope.client.module,
connection: Object.assign({}, scope.database.settings, {
user: scope.database.settings.username
})
}),
useNullAsDefault: true
});

knex.raw('select 1+1 as result').then(() => {
knex.raw(scope.client.database === 'postgres' ? "SELECT tablename FROM pg_tables WHERE schemaname='public'" : 'SELECT * FROM information_schema.tables').then((tables) => {
const selectQueries = {
mysql: 'SELECT tablename FROM pg_tables WHERE schemaname=\'public\'',
postgres: 'SELECT * FROM information_schema.tables',
sqlite: 'select * from sqlite_master'
};

knex.raw(selectQueries[scope.client.database]).then((tables) => {
knex.destroy();

const next = () => {
Expand Down
Loading