Удобства на улице для MySQL драйвера в Node.js

    Кто пишет на Node.js и использует MySQL, тот непременно знает, что наш дорогой товарищ Felix Geisendörfer три года назад совершил героический и самоотверженный подвиг: в одиночку разработал очень качественный драйвер для подключения к MySQL, нативно реализовав двоичный протокол этой СУБД на JavaScript. Потом к проекту подключились другие уважаемые товарищи, была реализована поддержка пулов соединений, кластеров, транзакций, восстановление при временной утере соединения и т.д. Сейчас драйвер является самым развитым, хорошо проработанным и активно поддерживаемым из того, что мы имеем в открытых репозитариях npm и github. Удивительно даже, что при хорошо проработанной низкоуровневой реализации, все удобства, которые предоставляет это драйвер для прикладного разработчика, сводятся к одному методу query. Для меня лично, этого очень мало, ну привык дедушка к удобствам для возврата скалярных значений, строк и столбцов в массивы, интроспекции структур БД. Так что, с удовольствием делюсь этими наработками с вами, мои дорогие Хабравчане, но предупреждаю, что все удобства будут на улице. Есть конечно вариант тесной интеграции с драйвером, но FelixGe желает оставить драйвер исключительно низкоуровневым, поэтому я остановился на варианте внешней библиотеки с добавлением к драйверу через примеси. В виде примесей, удобства попали и в платформу Impress, а так же, опубликованы как патч к драйверу. О функционале и вариантах использования далее.

    Удобства выборки данных


    Далее, под словом «возвращает» буду разуметь второй параметр callback, а не результат вызова функции.

    Выборка одной записи: connection.queryRow(sql, values, callback) возвращает хеш (ассоциативный массив), в котором имена полей становятся ключами (вместо того, чтобы получать массив в массиве через query).
    Пример
    connection.queryRow('SELECT * FROM Language where LanguageId=?', [3], function(err, row) {
        console.dir({queryRow:row});
        /* Example:
            queryRow: {
                LanguageId: 3,
                LanguageName: 'Russian',
                LanguageSign: 'ru',
                LanguageISO: 'ru',
                Caption: 'Русский'
            }
        */
    });
    

    Выборка скаляра (то есть единичного значения): connection.queryValue(sql, values, callback) возвращает одно значение (вместо того, чтобы получать массив в массиве с одним значением). Удобно при выборке одного пол из одной записи, например Id по имени с указанием LIMIT 1 или функций count(*), max(field) и т.д.
    Пример
    connection.queryValue('SELECT LanguageName FROM Language where LanguageId=?', [8],
        function(err, name) {
        console.dir({queryValue:name});
        /* Example:
            queryValue: 'Italiano'
        */
    });
    

    Выборка одной колонки: connection.queryCol(sql, values, callback) возвращает массив, заполненный значениями одного поля для каждой записи, из результата выполнения запроса. То есть, это выборка вертикальной колонки, в отличие от выборки горизонтальной записи queryRow.
    UPD: gelas убедил меня переименовать queryArray в queryCol по аналогии с queryRow.
    Пример
    connection.queryCol('SELECT LanguageSign FROM Language', [], function(err, result) {
        console.dir({queryCal:result});
        /* Example:
            queryArray: [ 'de', 'en', 'es', 'fr', 'it', 'pl', 'ru', 'ua' ]
        */
    });
    

    Выборка хеша: connection.queryHash(sql, values, callback) возвращает хеш (ассоциативный массив) двухуровневой вложенности, где ключи первого уровня — значения первого поля из результата выполнения запроса, а ключи второго уровня — все поля результата запроса (включая и первое).
    Пример
    connection.queryHash(
        'SELECT LanguageSign, LanguageId, LanguageName, Caption, LanguageISO FROM Language', [],
    function(err, result) {
        console.dir({queryHash:result});
        /* Example:
            queryHash: {
                en: {
                    LanguageSign: 'en',
                    LanguageId: 2,
                    LanguageName: 'English',
                    Caption: 'Английский',
                    LanguageISO: 'en' },
                ru: {
                    LanguageSign: 'ru',
                    LanguageId: 3,
                    LanguageName: 'Russian',
                    Caption: 'Русский',
                    LanguageISO: 'ru' },
                de: {
                    LanguageSign: 'de',
                    LanguageId: 7,
                    LanguageName: 'Deutsch',
                    Caption: 'Немецкий',
                    LanguageISO: 'de' },
                it: {
                    LanguageSign: 'it',
                    LanguageId: 8,
                    LanguageName: 'Italiano',
                    Caption: 'Итальянский',
                    LanguageISO: 'it'
                }
            }
        */
    });
    

    Выборка пар ключ/значение: connection.queryKeyValue(sql, values, callback) возвращает хеш (ассоциативный массив), где ключом будет первое поле из результата запроса запросе.
    Пример
    connection.queryKeyValue(
        'SELECT LanguageISO, LanguageName FROM Language', [], function(err, keyValue) {
        console.dir({queryKeyValue:keyValue});
        /* Example:
            keyValue: {
                en: 'English',
                ru: 'Russian',
                uk: 'Ukrainian',
                es: 'Espanol',
                fr: 'Francais',
                de: 'Deutsch',
                it: 'Italiano',
                pl: 'Poliski'
            }
        */
    });
    


    Удобства интроспекции


    То есть, удобства получения метаданных, структур и параметров базы для их анализа и автоматического построения логики или интерфейсов по работы с этой базой.

    Получение первичного ключа: connection.primary(table, callback) возвращает хеш (ассоциативный массив), с метаданными о первичном ключе, см. набор метаданных в примере.
    Пример
    connection.primary('Language', function(err, primary) {
        console.dir({primary:primary});
        /* Example:
            primary: {
                Table: 'language',
                Non_unique: 0,
                Key_name: 'PRIMARY',
                Seq_in_index: 1,
                Column_name: 'LanguageId',
                Collation: 'A',
                Cardinality: 9,
                Sub_part: null,
                Packed: null,
                Null: '',
                Index_type: 'BTREE',
                Comment: '',
                Index_comment: ''
            }
        */
    });
    

    Получение внешних ключей: connection.foreign(table, callback) возвращает хеш (ассоциативный массив), с двойной вложенностью, на первом уровне имена внешних ключей, а на втором — метаданные, описывающие этот ключ. Набор полей см. в примере.
    Пример
    connection.foreign('TemplateCaption', function(err, foreign) {
        console.dir({foreign:foreign});
        /* Example:
            foreign: {
                fkTemplateCaptionLanguage: {
                    CONSTRAINT_NAME: 'fkTemplateCaptionLanguage',
                    COLUMN_NAME: 'LanguageId',
                    ORDINAL_POSITION: 1,
                    POSITION_IN_UNIQUE_CONSTRAINT: 1,
                    REFERENCED_TABLE_NAME: 'language',
                    REFERENCED_COLUMN_NAME: 'LanguageId' }, 
                fkTemplateCaptionTemplate: {
                    CONSTRAINT_NAME: 'fkTemplateCaptionTemplate',
                    COLUMN_NAME: 'TemplateId',
                    ORDINAL_POSITION: 1,
                    POSITION_IN_UNIQUE_CONSTRAINT: 1,
                    REFERENCED_TABLE_NAME: 'template',
                    REFERENCED_COLUMN_NAME: 'TemplateId'
                }
        */
    });
    

    Получение ограничений целостности: connection.constraints(table, callback) возвращает хеш (ассоциативный массив), с двойной вложенностью, на первом уровне имена ограничений целостности, а на втором — метаданные, описывающие каждое ограничение. Набор полей см. в примере.
    Пример
    connection.constraints('TemplateCaption', function(err, constraints) {
        console.dir({constraints:constraints});
        /* Example:
            constraints: {
                fkTemplateCaptionLanguage: {
                    CONSTRAINT_NAME: 'fkTemplateCaptionLanguage',
                    UNIQUE_CONSTRAINT_NAME: 'PRIMARY',
                    REFERENCED_TABLE_NAME: 'Language',
                    MATCH_OPTION: 'NONE',
                    UPDATE_RULE: 'RESTRICT',
                    DELETE_RULE: 'CASCADE' },
                fkTemplateCaptionTemplate: {
                    CONSTRAINT_NAME: 'fkTemplateCaptionTemplate',
                    UNIQUE_CONSTRAINT_NAME: 'PRIMARY',
                    REFERENCED_TABLE_NAME: 'Template',
                    MATCH_OPTION: 'NONE',
                    UPDATE_RULE: 'RESTRICT',
                    DELETE_RULE: 'CASCADE'
                } 
            }
        */
    });
    

    Получение метаданных о полях таблицы: connection.fields(table, callback) возвращает массив с метаданными для каждого поля, в том чисте имя, тип, все модификаторы и флаги, комментарии (подробнее см. пример).
    Пример
    connection.fields('Language', function(err, fields) {
        console.dir({fields:fields});
        /* Example:
            fields: {
                LanguageId: {
                    Field: 'LanguageId',
                    Type: 'int(10) unsigned',
                    Collation: null,
                    Null: 'NO',
                    Key: 'PRI',
                    Default: null,
                    Extra: 'auto_increment',
                    Privileges: 'select,insert,update,references',
                    Comment: 'Id(EN),Код(RU)' },
                LanguageName: {
                    Field: 'LanguageName',
                    Type: 'varchar(32)',
                    Collation: 'utf8_general_ci',
                    Null: 'NO',
                    Key: 'UNI',
                    Default: null,
                    Extra: '',
                    Privileges: 'select,insert,update,references',
                    Comment: 'Name(EN),Имя(RU)'
                }, ...
            }
        */
    });
    

    Получение списка баз данных доступных по данному соединению: connection.databases(callback) возвращает массив имен баз (или «схем», как их иногда называют).
    Пример
    connection.databases(function(err, databases) {
        console.dir({databases:databases});
        /* Example:
            databases: [ 'information_schema', 'mezha', 'mysql', 'performance_schema', 'test' ]
        */
    });
    

    Получение списка таблиц для базы данных, выбранной текущей для данного соединения: connection.tables(callback) возвращает хеш (ассоциативный массив) двойной вложенности, где на первом уровне ключи — имена таблиц, а на втором — метаданные для каждой таблицы.
    Пример
    connection.tables(function(err, tables) {
        console.dir({tables:tables});
        /* Example:
            tables: {
                Language: {
                    TABLE_NAME: 'Language',
                    TABLE_TYPE: 'BASE TABLE',
                    ENGINE: 'InnoDB',
                    VERSION: 10,
                    ROW_FORMAT: 'Compact',
                    TABLE_ROWS: 9,
                    AVG_ROW_LENGTH: 1820,
                    DATA_LENGTH: 16384,
                    MAX_DATA_LENGTH: 0,
                    INDEX_LENGTH: 49152,
                    DATA_FREE: 8388608,
                    AUTO_INCREMENT: 10,
                    CREATE_TIME: Mon Jul 15 2013 03:06:08 GMT+0300 (Финляндия (лето)),
                    UPDATE_TIME: null,
                    CHECK_TIME: null,
                    TABLE_COLLATION: 'utf8_general_ci',
                    CHECKSUM: null,
                    CREATE_OPTIONS: '',
                    TABLE_COMMENT: '_Language:Languages(EN),Языки(RU)'
                }, ...
            }
        */
    });
    

    UPD: Получение списка таблиц для указанной базы данных: connection.databaseTables(database, callback) возвращает хеш (ассоциативный массив) двойной вложенности, где на первом уровне ключи — имена таблиц, а на втором — метаданные для каждой таблицы.
    Пример
    connection.databaseTables("databaseName", function(err, tables) {
        console.dir({databaseTables:tables});
        /* Example:
            tables: {
                Language: {
                    TABLE_NAME: 'Language',
                    TABLE_TYPE: 'BASE TABLE',
                    ENGINE: 'InnoDB',
                    VERSION: 10,
                    ROW_FORMAT: 'Compact',
                    TABLE_ROWS: 9,
                    AVG_ROW_LENGTH: 1820,
                    DATA_LENGTH: 16384,
                    MAX_DATA_LENGTH: 0,
                    INDEX_LENGTH: 49152,
                    DATA_FREE: 8388608,
                    AUTO_INCREMENT: 10,
                    CREATE_TIME: Mon Jul 15 2013 03:06:08 GMT+0300 (Финляндия (лето)),
                    UPDATE_TIME: null,
                    CHECK_TIME: null,
                    TABLE_COLLATION: 'utf8_general_ci',
                    CHECKSUM: null,
                    CREATE_OPTIONS: '',
                    TABLE_COMMENT: '_Language:Languages(EN),Языки(RU)'
                }, ...
            }
        */
    });
    

    Получение метаданных указанной таблицы: connection.tableInfo(table, callback) возвращает хеш (ассоциативный массив) с метаданными (подробнее см. в примере).
    Пример
    connection.tableInfo('Language', function(err, info) {
        console.dir({tableInfo:info});
        /* Example:
            tableInfo: {
                Name: 'language',
                Engine: 'InnoDB',
                Version: 10,
                Row_format: 'Compact',
                Rows: 9,
                Avg_row_length: 1820,
                Data_length: 16384,
                Max_data_length: 0,
                Index_length: 49152,
                Data_free: 9437184,
                Auto_increment: 10,
                Create_time: Mon Jul 15 2013 03:06:08 GMT+0300 (Финляндия (лето)),
                Update_time: null,
                Check_time: null,
                Collation: 'utf8_general_ci',
                Checksum: null,
                Create_options: '',
                Comment: ''
            }
        */
    });
    

    Получение метаданных о всех ключах данной таблицы: connection.indexes(table, callback) возвращает хеш (ассоциативный массив)? ключами первого уровня в нем являются имена ключей базы данных (), а ключами второго уровня — метаданные о каждом ключе (см. пример для подробного списка метаданных).
    Пример
    connection.indexes('Language', function(err, info) {
        console.dir({tableInfo:info});
        /* Example:
            indexes: {
                PRIMARY: {
                    Table: 'language',
                    Non_unique: 0,
                    Key_name: 'PRIMARY',
                    Seq_in_index: 1,
                    Column_name: 'LanguageId',
                    Collation: 'A',
                    Cardinality: 9,
                    Sub_part: null,
                    Packed: null,
                    Null: '',
                    Index_type: 'BTREE',
                    Comment: '',
                    Index_comment: '' },
                akLanguage: {
                    Table: 'language',
                    Non_unique: 0,
                    Key_name: 'akLanguage',
                    Seq_in_index: 1,
                    Column_name: 'LanguageName',
                    Collation: 'A',
                    Cardinality: 9,
                    Sub_part: null,
                    Packed: null,
                    Null: '',
                    Index_type: 'BTREE',
                    Comment: '',
                    Index_comment: ''
                }
            }
        */
    });
    

    Получение процессов на сервере MySQL: connection.processes(callback) возвращает массив хешей, где для каждого процесса даны его параметры.
    Пример
    connection.processes(function(err, processes) {
        console.dir({processes:processes});
        /* Example:
            processes: [ {
                ID: 62,
                USER: 'mezha',
                HOST: 'localhost:14188',
                DB: 'mezha',
                COMMAND: 'Query',
                TIME: 0,
                STATE: 'executing',
                INFO: 'SELECT * FROM information_schema.PROCESSLIST'
            }, {
                ID: 33,
                USER: 'root',
                HOST: 'localhost:39589',
                DB: null,
                COMMAND: 'Sleep',
                TIME: 1,
                STATE: '',
                INFO: null
            } ]
        */
    });
    

    Получение глобальных переменных MySQL: connection.globalVariables(callback)
    Пример
    connection.globalVariables(function(err, globalVariables) {
        console.dir({globalVariables:globalVariables});
        /* Example:
            globalVariables: {
                MAX_PREPARED_STMT_COUNT: '16382',
                MAX_JOIN_SIZE: '18446744073709551615',
                HAVE_CRYPT: 'NO',
                PERFORMANCE_SCHEMA_EVENTS_WAITS_HISTORY_LONG_SIZE: '10000',
                INNODB_VERSION: '5.5.32',
                FLUSH_TIME: '1800',
                MAX_ERROR_COUNT: '64',
                ...
            }
        */
    });
    

    Получение глобального статуса MySQL: connection.globalStatus(callback)
    Пример
    connection.globalStatus(function(err, globalStatus) {
        console.dir({globalStatus:globalStatus});
        /* Example:
            globalStatus: {
                ABORTED_CLIENTS: '54',
                ABORTED_CONNECTS: '2',
                BINLOG_CACHE_DISK_USE: '0',
                BINLOG_CACHE_USE: '0',
                BINLOG_STMT_CACHE_DISK_USE: '0',
                BINLOG_STMT_CACHE_USE: '0',
                BYTES_RECEIVED: '654871',
                BYTES_SENT: '212454927',
                COM_ADMIN_COMMANDS: '594',
                ...
            }
        */
    });
    

    Получение списка пользователей MySQL: connection.users(callback)
    Пример
    connection.users(function(err, users) {
        console.dir({users:users});
        /* Example:
            users: [
                {
                    Host: 'localhost',
                    User: 'root',
                    Password: '*90E462C37378CED12064BB3388827D2BA3A9B689',
                    Select_priv: 'Y',
                    Insert_priv: 'Y',
                    Update_priv: 'Y',
                    Delete_priv: 'Y',
                    Create_priv: 'Y',
                    Drop_priv: 'Y',
                    Reload_priv: 'Y',
                    Shutdown_priv: 'Y',
                    Process_priv: 'Y',
                    File_priv: 'Y',
                    Grant_priv: 'Y',
                    References_priv: 'Y',
                    Index_priv: 'Y',
                    Alter_priv: 'Y',
                    Show_db_priv: 'Y',
                    Super_priv: 'Y',
                    Create_tmp_table_priv: 'Y',
                    Lock_tables_priv: 'Y',
                    Execute_priv: 'Y',
                    Repl_slave_priv: 'Y',
                    Repl_client_priv: 'Y',
                    Create_view_priv: 'Y',
                    Show_view_priv: 'Y',
                    Create_routine_priv: 'Y',
                    Alter_routine_priv: 'Y',
                    Create_user_priv: 'Y',
                    Event_priv: 'Y',
                    Trigger_priv: 'Y',
                    Create_tablespace_priv: 'Y',
                    ssl_type: '',
                    ssl_cipher: <Buffer >,
                    x509_issuer: <Buffer >,
                    x509_subject: <Buffer >,
                    max_questions: 0,
                    max_updates: 0,
                    max_connections: 0,
                    max_user_connections: 0,
                    plugin: '',
                    authentication_string: ''
                }, ...
            ]
        */
    });
    


    Удобства генерации запросов


    Ну и совсем уже барские удобства, позволяющие генерировать SQL или полностью или отдельные WHERE выражения. Я сам не сторонник такого сахара, но иногда бывает нужно автоматизировать генерацию запросов, которые заранее не известны и я позволяю себе такую роскошь.

    Генерация условий: connection.where(conditions) работает синхронно, а не асинхронно, как другие функции, т.е. не использует callback. Возвращает построенное WHERE выражение SQL для conditions, описанных в стиле JSON. Нужно обязательно смотреть пример для понимания:
    Пример
    var where = connection.where({
        id: 5,
        year: ">2010",
        price: "100..200",
        level: "<=3",
        sn: "*str?",
        label: "str",
        code: "(1,2,4,10,11)"
    });
    console.dir(where);
    // Output: "id = 5 AND year > '2010' AND (price BETWEEN '100' AND '200') AND 
    // level <= '3' AND sn LIKE '%str_' AND label = 'str' AND code IN (1,2,4,10,11)"
    

    Выборка с условием: connection.select(table, whereFilter, callback)
    Пример
    connection.select('Language', '*', { LanguageId: "1..3" }, function(err, results) {
        console.dir({select:results});
    });
    

    Вставка записи: connection.insert(table, row, callback)
    Пример
    connection.insert('Language', {
        LanguageName: 'Tatar',
        LanguageSign:'TT',
        LanguageISO:'TT',
        Caption:'Tatar'
    }, function(err, recordId) {
        console.dir({insert:recordId});
    });
    

    Изменение записи: connection.update(table, row, callback)
    Пример
    connection.update('Language', {
        LanguageId: 25,
        LanguageName:'Tatarca',
        LanguageSign:'TT',
        LanguageISO:'TT',
        Caption:'Tatarca'
    }, function(err, affectedRows) {
        console.dir({update:affectedRows});
    });
    

    Вставка, если нет такой записи или изменение, если она уже есть: connection.upsert(table, row, callback)
    Пример
    connection.upsert('Language', {
        LanguageId: 25,
        LanguageName:'Tatarca',
        LanguageSign:'TT',
        LanguageISO:'TT',
        Caption:'Tatarca'
    }, function(err, affectedRows) {
        console.dir({upsert:affectedRows});
    });
    

    Получение количества записей в таблице по заданному фильтру или без фильтра: connection.count(table, whereFilter, callback)
    Пример
    connection.count('Language', { LanguageId: ">3" }, function(err, count) {
        console.dir({count:count});
        /* Example:
            count: 9
        */
    });
    

    Удаление записи или нескольких записей: connection.delete(table, whereFilter, callback)
    Пример
    connection.delete('Language', { LanguageSign:'TT' }, function(err, affectedRows) {
        console.dir({delete:affectedRows});
    });
    


    Варианты использования


    UPD: Предпочтительно использовать независимый модуль, с более высоким уровнем абстракции, чем просто драйвер, но не такого высокого уровня как ORM библиотеки. Я не хочу делать из этого громоздкую вещь, и хочу оставить возможность добавлять функционал к соединению через примеси (только нужные) группы функций.
    Делается это так:
    // Library dependencies
    var mysql = require('mysql'),
    	mysqlUtilities = require('utilities');
    
    var connection = mysql.createConnection({
    	host:     'localhost',
    	user:     'userName',
    	password: 'secret',
    	database: 'databaseName'
    });
    
    connection.connect();
    
    // Mix-in for Data Access Methods and SQL Autogenerating Methods
    mysqlUtilities.upgrade(connection);
    
    // Mix-in for Introspection Methods
    mysqlUtilities.introspection(connection);
    
    // Do something using utilities
    connection.queryRow(
    	'SELECT * FROM _Language where LanguageId=?', [3],
    	function(err, row) {
    		console.dir({queryRow:row});
    	}
    );
    
    // Release connection
    connection.end();
    


    Есть, также, патч к драйверу node-mysql со всеми, приведенными выше функциями: https://github.com/felixge/node-mysql

    И третий вариант использования, это сервер приложений Impress для Node.js, в который встроена эта библиотека. Две группы функций по выборке в Impress есть сразу у всех подключений к MySQL, они примешиваются автоматически, при открытии соединения, и только интроспекцию можно добавить через конфиг где нужно раскомментировать соответствующий плагин, чтобы набор плагинов для MySQL был такой:
    	plugins: {
    		require: [
    			"db",
    			"db.mysql",
    			"db.mysql.introspection",
    			...
    		]
    	},...
    

    Тогда плагин автоматически примешается к каждому соединению: if (db.mysql.introspection) db.mysql.introspection(connection);
    Тут искодники плагинов к Impress:

    Ссылки


    UPD: На момент написания статьи, было два вариант реализации (как патч к драйверу и как модули для Impress), но опрос убедительно показал, что имеет смысл потратить время и оформить эту библиотеку отдельным модулем. Это и было сделано уже к вечеру того же дня. Но опрос я не закрываю, чтоб определить все же степень полезности этих удобств.

    Основной вариант использования находится тут:
    В Github: https://github.com/tshemsedinov/node-mysql-utilities
    В репозитории nmp: https://npmjs.org/package/mysql-utilities
    Нужны ли вам подобные удобства?
    Стоит ли выделить их в отдельную библиотеку и опубликовать отдельно от драйвера и от Impress?
    • 89%Да, если можно, сделайте это (UPD: уже готово)49
    • 10.9%Не стоит терять время6

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 26
    • 0
      Библиотека выделена отдельным модулем:
      https://github.com/tshemsedinov/node-mysql-utilities
      https://npmjs.org/package/mysql-utilities
      • 0
        Замечательная идея и удобная реализация, всячески поддерживаю.

        Но без тестов пользоваться этим будет только совершенно безответственный разработчик.
        • 0
          В каком смысле без тестов? Я тестирую, хотите — тестируйте сами )
          • 0
            В смысле в репе нет тестов, дабы проверить у себя ожидаемое поведение.

            А топику плюс.
            • 0
              Тут так просто у себя и не протестировать, это нужно разворачивать MySQL, создавать в нем БД с определенной структурой, для тестирования, заполнять ее тестовыми данными и потом над этой БД выполнять тесты. Каждый раз при запуске БД нужно приводить в первоначальное состояние. У меня это развернуто и настроено, имеет ли смысл разворачивать тесты каждому разработчику отдельно? Возможно, со временем, можно настроить Travis для этого.
        • +1
          Вышла версия 0.0.4 и появился очень нужный функционал: события, чтоб прицепиться на результат любых запросов и для выявления долгих запросов. Потом можно писать в лог или иначе сигнализировать. Пример:
          // запрос, исполняемый дольше этого значения, будет генерировать событие 'slow'
          connection.slowTime = 100; // значение в миллисекундах, по умолчанию 2000
          
          connection.on('query', function(err, res, fields, query) {
          	console.dir({onQuery:{err:err, query:query.sql}});
          });
          
          connection.on('slow', function(err, res, fields, query, executionTime) {
          	console.dir({onSlow:{err:err, executionTime:executionTime, query:query.sql}});
          });
          
          • 0
            а как это красоту с pool'ом использовать? потому как один conn в node,js не трендово
            • 0
              Берете node-mysql, делаете пул конекшенов, как там на странице написано, потом ко всем конекшенам применяете примешивание (одно или оба): mysqlUtilities.upgrade(connection); mysqlUtilities.introspection(connection); После чего можно юзать все перечисленные функции.
              • 0
                хорошо, если конекшен из пула отвалился от мускла, и пул его пересоздал, что тогда?

                ps. тем более пул не создает сходу максимум конекшенов, а открывает их по мере необходимости
                • 0
                  не, можно конечно повесить на

                  pool.on('connection', function(connection) {
                  //bla-bla-bla
                  });

                  надо только посмотреть как он ведет себя при реконекте
                  • 0
                    Именно, если что-то не пойдет — пишите, исправим.
                    • 0
                      та, «пойдет не так»… уже на продакшене!
                      • 0
                        А протестировать до продакшена нельзя или как всегда, на вчера? )
                        • 0
                          на продакшене и протестится :)

                          а почему пул не напрягали, неужели через один конекшен шпарите?
                          • 0
                            Пока проект, в котором это используется, только в разработке, еще нет нагрузок. Но я не вижу причин, почему это не должно работать на больших нагрузках, конекшены одинаковые, что в пуле, что так. Я предлагал это в драйвер вставить, в конструктор конекшены, чтобы была опция драйвера, порождать конекшены сразу с примесями этих утилит.
                            • 0
                              нет, в констрактор конекшена как раз лезть не стоит, imho. может connection.upsert лучше назвать connection.replace?
                              • 0
                                UPSERT это общепринятый устоявшийся термин, он давно используется в различных библиотеках-обертках для реляционных БД, кроме того, в родных средства доступа к MongoDB и SQL Azure, и означает UPDATE or INSERT — изменение записи в случае, если она есть и вставка новой, если записи до этого не существовало. Совершенно другое значение имеет REPLACE — это синоним UPDATE — замена и изменение.
                                • 0
                                  в терминах mysql, replace — это как раз и есть " изменение записи в случае, если она есть и вставка новой, если записи до этого не существовало".
                                  • 0
                                    Это нестандартный SQL-оператор, в других SQL-ных СУБД он заменяет подстроку в строке. Само слово REPLACE имеет известный смысл, совершенно не сочетающийся с созданием новых записей, почему вдруг ЗАМЕНА должна создавать что-то а не модифицировать.
                                    • 0
                                      функция replace и в mysql заменять подстроку в строке, но т.к. либа исключительно под mysql, я и предложил назвать метод более привычным в терминах mysql оператором. ну нет, так нет, теоретически-то пофигу
                                      • 0
                                        У нас такие же либы готовятся к другим СУБД, чтоб интерфейс был одинаковый.
                                        • 0
                                          в общем, посмотрели мы, со стримами не работает, синхронные лупы внутри, три обязательных параметра в функции (sql, callback не прокатывает) и т.д.… короче написали своё, спасибо за вдохновение :)
                                          • 0
                                            Ну спасибо это много, а вот указать, где именно синхронные лупы — в самый раз. Параметр values исправил на опциональный. Со стримами что именно не работает?
                                            • 0
                                              Так что с пулами не разобрались?
                                              • 0
                                                С пулами вроде все ж хорошо работает, вот даже кто-то пример положил. Можно применять миксин к любому соединению, хоть к одиночному, хоть входящему в пул: github.com/tshemsedinov/node-mysql-utilities/issues/5
                                                • 0
                                                  шикарно — извините за некропост.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.