精华内容
下载资源
问答
  • 添加或删除应用程序定义的HandlerRoutine从调用进程的处理函数列表功能。Win32 API功能:添加或删除应用程序定义的HandlerRoutine从调用进程的处理函数列表功能。函数原型:BOOL SetConsoleCtrlHandler(PHANDLER_...

    添加或删除应用程序定义的HandlerRoutine从调用进程的处理函数列表功能。

    Win32 API
    功能:
    添加或删除应用程序定义的HandlerRoutine从调用进程的处理函数列表功能。
    函数原型:
    BOOL SetConsoleCtrlHandler(
    PHANDLER_ROUTINE HandlerRoutine, // 回调函数
    BOOL Add // 表示添加还是删除
    );
    返回值:BOOL类型
    参数
    参数HandlerRoutine:一个应用程序定义的指针HandlerRoutine 功能要添加或删除。 这个参数可以是NULL。
    参数Add:如果此参数为TRUE,处理程序添加,如果是假的,该处理程序将被删除。
    返回值:
    如果函数失败,返回值为0.否则,返回一个非0值。 若想获得更多错误信息,调用GetLastError函数 。
    说白了,就是一个系统调用。
    第一个参数是函数指针,就是上面的那个函数。第二个参数是标志,如果为TRUE那么就安装钩子,如果为FALSE那么删除钩子。

    利用Windows API 拦截Console的手动关闭事件的消息,来实现,下面我给出的是一个完整的事例:

    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading;
    
    /************************************************************************/
    /* CSharp控制台关闭事件改写
     * Powered by:testcs_dn
     * Blog:http://blog.csdn.net/testcs_dn
     */
    /************************************************************************/
    namespace CSharp控制台关闭事件改写
    {
        /// <summary>
        /// 控制台关闭事件改写
        /// 这里展示了Windows API SetConsoleCtrlHandler函数的应用,同时展示了线程计时器的使用;
        /// Author:testcs_dn
        /// Date:2015-01-03
        /// </summary>
        class Program
        {
            /// <summary>
            /// 计时器回调函数,在这里处理计时时间是否到达的判断以及要做的事情;
            /// </summary>
            /// <param name="obj"></param>
            public static void workOvertimeTimerCallback(object obj)
            {
                DateTime dt = DateTime.Now;
                if (dt.Hour == 16 && dt.Minute > 0)
                {
                    Console.WriteLine("ok");
                }
            }
    
            //计时器变量
            public static System.Threading.Timer workOvertimeTimer = null;
    
            //定义处理程序委托
            delegate bool ConsoleCtrlDelegate(int dwCtrlType);
    
            const int CTRL_CLOSE_EVENT = 2;
    
            //导入SetCtrlHandlerHandler API
            [DllImport("kernel32.dll")]
            private static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
            static void Main(string[] args)
            {
                ConsoleCtrlDelegate newDelegate = new ConsoleCtrlDelegate(HandlerRoutine);
                if (SetConsoleCtrlHandler(newDelegate, true))
                {
                    //初始化计时器
                    workOvertimeTimer = new System.Threading.Timer(new TimerCallback(workOvertimeTimerCallback), null, 1000, 10000);
                    //这里执行你自己的任务,我举例输出“...”,为了展示长时间的任务,我用了一个死循环;
                    //避免输出太多,使用了Sleep;
                    //注意:Sleep的时间不可太长,否则可能影响Console.ReadKey(),导致不能接收用户输入;
                    while (true)
                    {
                        Console.WriteLine("...");
                        Thread.Sleep(100);
                    }
                }
                else
                {
                    Console.WriteLine("抱歉,API注入失败,按任意键退出!");
                    Console.ReadKey();
                }
            }
    
            /// <summary>
            /// 处理程序例程,在这里编写对指定事件的处理程序代码
            /// </summary>
            /// <param name="CtrlType"></param>
            /// <returns></returns>
            static bool HandlerRoutine(int CtrlType)
            {
                switch (CtrlType)
                {
                    case CTRL_CLOSE_EVENT:       //用户要关闭Console了
                        Console.WriteLine();
                        Console.WriteLine("任务还没有完成,确认要退出吗?(Y/N)");
                        ConsoleKeyInfo ki = Console.ReadKey();
                        return ki.Key == ConsoleKey.Y;
                    default:
                        return true;
                }
            }
        }
    }
    
    参考:http://msdn.microsoft.com/en-us/library/ms686016(v=vs.85).aspx

    展开全文
  • QT中的改写窗口的关闭事件

    千次阅读 2019-04-09 14:12:19
    今天在项目中遇到需要重新修改这一个直接关闭窗口的功能,所以就要重载这个函数QCloseEvent。 二、 头文件: #include <QCloseEvent> 在.h文件中protected: void closeEvent(QCloseEvent *eve...

    一、背景:qt中的窗口不能像office一样,在点击关闭按钮的时候,会弹出是否保存,取消的界面,所有就要重新写关闭事件。
    今天在项目中遇到需要重新修改这一个直接关闭窗口的功能,所以就要重载这个函数QCloseEvent。
    二、

    头文件:
    
    #include <QCloseEvent>
    
    在.h文件中protected:
    
    void closeEvent(QCloseEvent *event);
    
    .cpp中:
    	void 类名::closeEvent(QCloseEvent *event) //根据不同的需求进行添加,比如:
    	{
    		if(IsSaved)
        	{
            qApp->quit();
        	}
        	int ret = QMessageBox::warning(0,"Warning",QString("%1 has been changed!\n Do you want to save it?").arg(filepath),QMessageBox::Yes |QMessageBox::No|QMessageBox::Cancel,QMessageBox::No);
        	if(ret == QMessageBox::Yes) //如果选择保存文件,则执行保存操作
        	{
            	do_file_Save(event);//保存文件
        	}
        	else if(ret == QMessageBox::Cancel)
        	{
            	event->ignore();;
        	}
        	else
        	{
           		qApp->quit();//再退出系统,qApp是指向应用程序的全局指针
        	}
    	}
    
    展开全文
  • 改写后代码如下: static int wpa_ctrl_command( struct wpa_ctrl *ctrl, char *buf, const char *cmd) { return _wpa_ctrl_command(ctrl, buf, cmd, 0 ); } static int _wpa_ctrl_command...

    上一篇博客我们一起看了怎样使用wpas的命令后,接下来就利用这些命令来实现我们的代码。这些命令的实现都在wpa_cli.c文件中,以status命令为例,发生如下调用:

    static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
    {
        int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0;
        return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS");
    }
    
    wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
        _wpa_ctrl_command(ctrl, cmd, 1);
            char buf[2048];
            wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf,                                    
                                    &len,wpa_cli_msg_cb);
            if (print)
                printf("%s", buf);

    从上面可以看出,最终是调用wpa_ctrl_request函数完成命令,而最终结果是存在buf里,通过函数参数print来觉得是否输出到终端。所以我们最终需要取得的结果就是buf,只要拿到buf内容,我们就可以分析其内容实现我们需要的代码。为了实现上述目的,需要对源码进行下修改:我们把输出buf作为参数传入_wpa_ctrl_command函数,这样就可以拿到输出内容了。改写后代码如下:

    static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *buf,  const char *cmd)
    {
        return _wpa_ctrl_command(ctrl, buf, cmd, 0);
    }
    static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *buf, const char *cmd, int print)
    {
        char ret_buf[2048];
        int ret;
        size_t ret_len;
    
        ret_len = sizeof(ret_buf) - 1;
        ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), ret_buf, &ret_len,
                               wpa_cli_msg_cb);
        if (ret == -2)
        {
            printf("'%s' command timed out.\n", cmd);
            return -2;
        }
        else if (ret < 0)
        {
            printf("'%s' command failed.\n", cmd);
            return -1;
        }
    
        ret_buf[ret_len] = '\0';
        memcpy(buf, ret_buf, 2048);
    
        if (print)
        {
            ret_buf[ret_len] = '\0';
            printf("%s", ret_buf);
        }
        return 0;
    }
    static void wpa_cli_msg_cb(char *msg, size_t len)
    {
        printf("%s\n", msg);
    }

    对于各个命令呢,我们同样需要加入buf,还是以status为例:

    static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, char *buf,  int argc, char *argv[])
    {
        int verbose = argc > 0 && strcmp(argv[0], "verbose") == 0;
        return wpa_ctrl_command(ctrl, buf, verbose ? "STATUS-VERBOSE" : "STATUS");
    }

    这样各个命令就达到了我们的目的。最后我们对命令进行一下封装,方便统一管理。

    #define CFG_MAXARGS 10
    
    /**
     * @brief 对wpa_supplicant中处理命令进行封装
     */
    class HanleCmd : public QObject
    {
        Q_OBJECT
    public:
        HanleCmd();
        void printCmd();
        int hanleCmd(struct wpa_ctrl *ctrl, char *buf, char *cmd);
    
    private:
        //命令封装
        struct wpa_cli_cmd
        {
            const char *cmd;  //命令名称
            int (*handler)(struct wpa_ctrl *ctrl, char *buf, int argc, char *argv[]);  //命令处理函数
        };
    
        QList<wpa_cli_cmd> listCmd;  //命令链表
    
        //私有函数
        void addCmd();
        int wpaRequest(struct wpa_ctrl *ctrl, char *buf, int argc, char *argv[]);
        int parseLine (char *line, char *argv[]);
    };
    HanleCmd::HanleCmd()
    {
        qDebug("init cmd list");
        addCmd();
    }
    
    void HanleCmd::addCmd()
    {
        struct wpa_cli_cmd cmd;
    
        //状态命令
        cmd.cmd = "status";
        cmd.handler = wpa_cli_cmd_status;
        listCmd.append(cmd);
    
        //扫描命令
        cmd.cmd = "scan";
        cmd.handler = wpa_cli_cmd_scan;
        listCmd.append(cmd);
    
        //扫描结果命令
        cmd.cmd = "scan_results";
        cmd.handler = wpa_cli_cmd_scan_results;
        listCmd.append(cmd);
    
        //选择AP命令
        cmd.cmd = "select_network";
        cmd.handler = wpa_cli_cmd_select_network;
        listCmd.append(cmd);
    
        //增加AP命令
        cmd.cmd = "add_network";
        cmd.handler = wpa_cli_cmd_add_network;
        listCmd.append(cmd);
    
        //列出配置文件中已经保存的AP信息
        cmd.cmd = "list_network";
        cmd.handler = wpa_cli_cmd_list_networks;
        listCmd.append(cmd);
    
        //设置AP
        cmd.cmd = "set_network";
        cmd.handler = wpa_cli_cmd_set_network;
        listCmd.append(cmd);
    
        //移除AP
        cmd.cmd = "remove_network";
        cmd.handler = wpa_cli_cmd_remove_network;
        listCmd.append(cmd);
    
        //使能某个AP
        cmd.cmd = "enable_network";
        cmd.handler = wpa_cli_cmd_enable_network;
        listCmd.append(cmd);
    
        //关闭某个AP
        cmd.cmd = "disable_network";
        cmd.handler = wpa_cli_cmd_disable_network;
        listCmd.append(cmd);
    
        //保存配置
        cmd.cmd = "save_config";
        cmd.handler = wpa_cli_cmd_save_config;
        listCmd.append(cmd);
    }
    int HanleCmd::parseLine (char *line, char *argv[])
    {
        int nargs = 0;
    
        while (nargs < CFG_MAXARGS)
        {
    
            /* skip any white space */
            while ((*line == ' ') || (*line == '\t'))
            {
                ++line;
            }
    
            if (*line == '\0')      /* end of line, no more args    */
            {
                argv[nargs] = NULL;
                return (nargs);
            }
    
            argv[nargs++] = line;   /* begin of argument string */
    
            /* find end of string */
            while (*line && (*line != ' ') && (*line != '\t'))
            {
                ++line;
            }
    
            if (*line == '\0')      /* end of line, no more args    */
            {
                argv[nargs] = NULL;
                return (nargs);
            }
    
            *line++ = '\0';     /* terminate current arg     */
        }
    
        return (nargs);
    }
    int HanleCmd::hanleCmd(struct wpa_ctrl *ctrl, char *buf, char *cmd)
    {
        int argc;
        char bufTmp[1024];
        char *argv[CFG_MAXARGS];
    
        strncpy(bufTmp, cmd, 1024);
        bufTmp[1023] = '\0';
    
        argc = parseLine(bufTmp, argv);
        return wpaRequest(ctrl, buf, argc, argv);
    }
    

    封装完毕后,只需要在执行某个命令时候调用如下代码即可:

    char buf[2048];
    handleCmd->hanleCmd(ctrl_conn, buf,  "status");

    这样buf中存入的就是status命令的结果了。

    至于参数中的ctrl_conn是底层的操作接口,需要读者自己去分析下wpa_cli的源码,这里只贴一下代码:

    int WifiService::initWpa()
    {
        qDebug()<<"initWpa";
        //1.变量初始化
        int conectNum = 0;
        ctrl_iface = NULL;
        ctrl_conn = NULL;
        monitor_conn = NULL;
        ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
    
        //2.与wpa_supplicant建立连接
        while(true)
        {
            wpa_cli_get_default_ifname();
            if(ctrl_iface == NULL){
                qDebug("failed to wpa_cli_get_default_ifname");
                return -1;
            }
    
            wpa_cli_open_connection(ctrl_iface);
            if (ctrl_conn || monitor_conn){  //ActionThread未启用情况下只用ctrl_conn就可以
                qDebug("wpa_supplicant connection established");
                break;
            }else{
                if(conectNum++ >=2){  //最多试三次
                    qDebug("wpa_supplicant connection established err");
                    return -1;
                }
                qDebug("wpa_supplicant connection established err, we will try agin");
                usleep(10000);
                continue;
            }
        }
        return 0;
    }
    展开全文
  • 修改键盘映射,如果你的键盘里哪个键坏掉了,可以用这个临时把另一个好的键修改为坏掉的那个键的功能。是个很好的东西,另外,如果因为某些原因觉得键位设置不顺手,可以用这个工具来修改一下,关掉后就复原了,无须...
  • 我想要实现的是在窗口A上实例化调用窗口B,并且第一次调用B时按钮1的单击功能关闭窗口B,第二次调用时B按钮1的单击功能是弹出一个对话框,第三次调用B时是按钮1又是不同于前两种的其他功能。请问有这个有可能实现...
  • shardingjdbc (五)-SQL改写

    千次阅读 2018-06-06 13:15:52
    一 序 上一篇在《SQL路由实现》 提了SQL改写。这里跟路由紧密结合,是在路由之后的步骤。路由解决了分库分表去查那些,改写负责一些如查询结果需要聚合,对SQL进行调整,生成分库分表后的执行SQL。二 sqltokenSQL...

    一 序

     上一篇在《SQL路由实现》  提了SQL改写。这里跟路由紧密结合,是在路由之后的步骤。路由解决了分库分表去查那些,改写负责一些如查询结果需要聚合,对SQL进行调整,生成分库分表后的执行SQL。

    二 sqltoken

    SQL改写的源码在core模块的io.shardingjdbc.core.rewrite目录下,分为SQLBuilder和SQLRewriteEngine。

    SQLToken,SQL标记对象接口,属于SQL解析部分,在io.shardingjdbc.core.parsing.parser.token目录下。SQLRewriteEngine 基于 SQLToken 实现 SQL改写。SQL解析器在 SQL解析过程中,很重要的一个目的是标记需要SQL改写的部分,也就是 SQLToken。


    GeneratedKeyToken 自增主键标记对象
        插入SQL自增列不存在:INSERT INTO t_order(nickname) VALUES ... 中没有自增列 order_id
    TableToken 表标记对象
        查询列的表别名:SELECT o.order_id 的 o
    ItemsToken 选择项标记对象
        AVG查询列:SELECT AVG(price) FROM t_order 的 AVG(price)
        ORDER BY 字段不在查询列:SELECT order_id FROM t_order ORDER BY create_time 的 create_time
        GROUP BY 字段不在查询列:SELECT COUNT(order_id) FROM t_order GROUP BY user_id 的 user_id
        自增主键未在插入列中:INSERT INTO t_order(nickname) VALUES ... 中没有自增列 order_id
    OffsetToken 分页偏移量标记对象
        分页有偏移量,但不是占位符 ?
    RowCountToken 分页长度标记对象
        分页有长度,但不是占位符 ?
    OrderByToken 排序标记对象
        有 GROUP BY 条件,无 ORDER BY 条件:SELECT COUNT(*) FROM t_order GROUP BY order_id 的 order_id

    三 SQL 改写

    SQLRewriteEngine#rewrite() 实现了 SQL改写 功能。

    /**
         * rewrite SQL.
         *
         * @param isRewriteLimit is rewrite limit
         * @return SQL builder
         */
        public SQLBuilder rewrite(final boolean isRewriteLimit) {
            SQLBuilder result = new SQLBuilder();
            if (sqlTokens.isEmpty()) {
                result.appendLiterals(originalSQL);
                return result;
            }
            int count = 0;
            // 排序SQLToken,按照 beginPosition 递增
            sortByBeginPosition();
            for (SQLToken each : sqlTokens) {
                if (0 == count) {
                    //第一次处理:截取从原生SQL的开始位置到第一个token起始位置之间的内容,
                    //例如"SELECT x.id FROM table_x x LIMIT 2, 2"这条SQL的第一个token是TableToken,即table_x所在位置,所以截取内容为"SELECT x.id FROM "
                    result.appendLiterals(originalSQL.substring(0, each.getBeginPosition()));
                }
                 // 拼接每个SQLToken,后面有解释
                if (each instanceof TableToken) {
                    appendTableToken(result, (TableToken) each, count, sqlTokens);
                } else if (each instanceof IndexToken) {
                    appendIndexToken(result, (IndexToken) each, count, sqlTokens);
                } else if (each instanceof ItemsToken) {
                    appendItemsToken(result, (ItemsToken) each, count, sqlTokens);
                } else if (each instanceof RowCountToken) {
                    appendLimitRowCount(result, (RowCountToken) each, count, sqlTokens, isRewriteLimit);
                } else if (each instanceof OffsetToken) {
                    appendLimitOffsetToken(result, (OffsetToken) each, count, sqlTokens, isRewriteLimit);
                } else if (each instanceof OrderByToken) {
                    appendOrderByToken(result, count, sqlTokens);
                }
                count++;
            }
            return result;
        }
     private void sortByBeginPosition() {
            Collections.sort(sqlTokens, new Comparator<SQLToken>() {
                
                @Override
                public int compare(final SQLToken o1, final SQLToken o2) {
                    return o1.getBeginPosition() - o2.getBeginPosition();
                }
            });
        }

    SQLBuilder,SQL构建器。主要属性有:

    public final class SQLBuilder {
        
        private final List<Object> segments;
        
        private StringBuilder currentSegment;

    下面是每种token的实现方式:

    3.1 tabletoken

    private void appendTableToken(final SQLBuilder sqlBuilder, final TableToken tableToken, final int count, final List<SQLToken> sqlTokens) {
            sqlBuilder.appendTable(tableToken.getTableName().toLowerCase());
            int beginPosition = tableToken.getBeginPosition() + tableToken.getOriginalLiterals().length();
            appendRest(sqlBuilder, count, sqlTokens, beginPosition);
        }

    1 gettablename处理了特殊字符

        public String getTableName() {
            return SQLUtil.getExactlyValue(originalLiterals);
        }
      public static String getExactlyValue(final String value) {
            return null == value ? null : CharMatcher.anyOf("[]`'\"").removeFrom(value);
        }
       //把TableToken也要添加到SQLBuilder中(List<Object> segments)
        public void appendTable(final String tableName) {
            segments.add(new TableToken(tableName));
            currentSegment = new StringBuilder();
            segments.add(currentSegment);
        }

    3.2 IndexToken

     private void appendIndexToken(final SQLBuilder sqlBuilder, final IndexToken indexToken, final int count, final List<SQLToken> sqlTokens) {
            String indexName = indexToken.getIndexName().toLowerCase();
            String logicTableName = indexToken.getTableName().toLowerCase();
            if (Strings.isNullOrEmpty(logicTableName)) {
                logicTableName = shardingRule.getLogicTableName(indexName);
            }
            sqlBuilder.appendIndex(indexName, logicTableName);
            int beginPosition = indexToken.getBeginPosition() + indexToken.getOriginalLiterals().length();
            appendRest(sqlBuilder, count, sqlTokens, beginPosition);
        }

    主要是把索引名与逻辑表拼在一起。把indextoken加入到SQLBuilder

        public void appendIndex(final String indexName, final String tableName) {
            segments.add(new IndexToken(indexName, tableName));
            currentSegment = new StringBuilder();
            segments.add(currentSegment);
        }

    3.3 ItemsToken

     private void appendItemsToken(final SQLBuilder sqlBuilder, final ItemsToken itemsToken, final int count, final List<SQLToken> sqlTokens) {
            for (String item : itemsToken.getItems()) {
                sqlBuilder.appendLiterals(", ");
                sqlBuilder.appendLiterals(SQLUtil.getOriginalValue(item, databaseType));
            }
            int beginPosition = itemsToken.getBeginPosition();
            appendRest(sqlBuilder, count, sqlTokens, beginPosition);
        }

    举例:avg:  SELECT  AVG(order_id)       FROM        t_order        WHERE     user_id =#{userId,jdbcType=INTEGER}

    执行完appendItemsToken 后:

     item [SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        ]

    其他的当逻辑SQL有order by,group by这样的特殊条件时,需要在select的结果列中增加一些结果列。

    3.4 OffsetToken

     private void appendLimitOffsetToken(final SQLBuilder sqlBuilder, final OffsetToken offsetToken, final int count, final List<SQLToken> sqlTokens, final boolean isRewrite) {
            sqlBuilder.appendLiterals(isRewrite ? "0" : String.valueOf(offsetToken.getOffset()));
            int beginPosition = offsetToken.getBeginPosition() + String.valueOf(offsetToken.getOffset()).length();
            appendRest(sqlBuilder, count, sqlTokens, beginPosition);
        }
         当分页跨分片时,需要每个分片都查询后在内存中进行聚合。此时 isRewrite = true。为什么是 "0" 开始呢?每个分片在 [0, offset) 的记录可能属于实际分页结果,因而查询每个分片需要从 0 开始。

        当分页单分片时,则无需重写,该分片执行的结果即是最终结果。

    3.5 RowCountToken

    private void appendLimitRowCount(final SQLBuilder sqlBuilder, final RowCountToken rowCountToken, final int count, final List<SQLToken> sqlTokens, final boolean isRewrite) {
            SelectStatement selectStatement = (SelectStatement) sqlStatement;
            Limit limit = selectStatement.getLimit();
            if (!isRewrite) {
                //不需要重写,路有结果为单分片,直接append rowCount的值即可;
                sqlBuilder.appendLiterals(String.valueOf(rowCountToken.getRowCount()));
            } else if ((!selectStatement.getGroupByItems().isEmpty() // [1.1] 跨分片分组需要在内存计算,可能需要全部加载
            || !selectStatement.getAggregationSelectItems().isEmpty()) && !selectStatement.isSameGroupByAndOrderByItems()) {
               //跨分片聚合列需要在内存计算,可能需要全部加载,如果排序不一致(即各分片没有排序好结果),可能需要全部加载
                sqlBuilder.appendLiterals(String.valueOf(Integer.MAX_VALUE));
            } else {
                 // 路由结果为多分片,重写为  offset+rowCount;
                sqlBuilder.appendLiterals(String.valueOf(limit.isNeedRewriteRowCount() ? rowCountToken.getRowCount() + limit.getOffsetValue() : rowCountToken.getRowCount()));
            }
            int beginPosition = rowCountToken.getBeginPosition() + String.valueOf(rowCountToken.getRowCount()).length();
            appendRest(sqlBuilder, count, sqlTokens, beginPosition);
        }

    条件2可能变成必须的前提是 GROUP BY 和 ORDER BY 排序不一致。如果一致,各分片已经排序完成,无需内存中排序。

    分页补充

    OffsetToken、RowCountToken 只有在分页对应位置非占位符 ? 才存在。当对应位置是占位符时,会对分页条件对应的预编译 SQL 占位符参数进行重写,整体逻辑和 OffsetToken、RowCountToken 是一致的。以下为ParsingSQLRouter.route里面调用

     private void processLimit(final List<Object> parameters, final SelectStatement selectStatement, final boolean isSingleRouting) {
            if (isSingleRouting) {
                selectStatement.setLimit(null);
                return;
            }
            boolean isNeedFetchAll = (!selectStatement.getGroupByItems().isEmpty() || !selectStatement.getAggregationSelectItems().isEmpty()) && !selectStatement.isSameGroupByAndOrderByItems();
            selectStatement.getLimit().processParameters(parameters, isNeedFetchAll);
        }

    3.6 OrderByToken

     数据库里,当无 ORDER BY条件 而有 GROUP BY 条件时候,会使用 GROUP BY条件将结果升序排序:
    SELECT order_id FROM t_order GROUP BY order_id 等价于 SELECT order_id FROM t_order GROUP BY order_id ORDER BY order_id ASC

    SELECT order_id FROM t_order GROUP BY order_id DESC 等价于 SELECT order_id FROM t_order GROUP BY order_id ORDER BY order_id DESC

     private void appendOrderByToken(final SQLBuilder sqlBuilder, final int count, final List<SQLToken> sqlTokens) {
            SelectStatement selectStatement = (SelectStatement) sqlStatement;
            StringBuilder orderByLiterals = new StringBuilder();
            orderByLiterals.append(" ").append(DefaultKeyword.ORDER).append(" ").append(DefaultKeyword.BY).append(" ");
            int i = 0;
            for (OrderItem each : selectStatement.getOrderByItems()) {
                String columnLabel = SQLUtil.getOriginalValue(each.getColumnLabel(), databaseType);
                if (0 == i) {
                    orderByLiterals.append(columnLabel).append(" ").append(each.getType().name());
                } else {
                    orderByLiterals.append(",").append(columnLabel).append(" ").append(each.getType().name());
                }
                i++;
            }
            orderByLiterals.append(" ");
            sqlBuilder.appendLiterals(orderByLiterals.toString());
            int beginPosition = ((SelectStatement) sqlStatement).getGroupByLastPosition();
            appendRest(sqlBuilder, count, sqlTokens, beginPosition);
        }

    3.7 appendrest

    private void appendRest(final SQLBuilder sqlBuilder, final int count, final List<SQLToken> sqlTokens, final int beginPosition) {
        // 如果SQL解析后只有一个token,那么结束位置(endPosition)就是sql末尾;否则结束位置就是到下一个token的起始位置
        int endPosition = sqlTokens.size() - 1 == count ? originalSQL.length() : sqlTokens.get(count + 1).getBeginPosition();
        sqlBuilder.appendLiterals(originalSQL.substring(beginPosition, endPosition));
    }
    所有重写最后都会内部调用appendRest(),即附加上余下部分内容,这个余下部分内容是指从当前处理的token到下一个token之间的内容。

    四 生成SQL

    重写完后,调用SQLBuilder的toString()方法生成重写后最终的SQL语句;

    /**
         * Convert to SQL string.
         *
         * @param tableTokens table tokens
         * @return SQL string
         */
        public String toSQL(final Map<String, String> tableTokens) {
            StringBuilder result = new StringBuilder();
            for (Object each : segments) {
                if (each instanceof TableToken && tableTokens.containsKey(((TableToken) each).tableName)) {
                    result.append(tableTokens.get(((TableToken) each).tableName));
                } else if (each instanceof IndexToken) {
                    IndexToken indexToken = (IndexToken) each;
                    result.append(indexToken.indexName);
                    String tableName = tableTokens.get(indexToken.tableName);
                    if (!Strings.isNullOrEmpty(tableName)) {
                        result.append("_");
                        result.append(tableName);
                    }
                } else {
                    result.append(each);
                }
            }
            return result.toString();
        }

    举例:上面的处理完之后结果为

     sqlbuilder:[[SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        , t_order,         WHERE     user_id =?]

           最后,再来看下SQL改写在路由的大流程ParsingSQLRouter 主#route()

    @Override  
       public SQLRouteResult route(final String logicSQL, final List<Object> parameters, final SQLStatement sqlStatement) {  
           SQLRouteResult result = new SQLRouteResult(sqlStatement);  
           //处理 插入SQL 主键字段  
           if (sqlStatement instanceof InsertStatement && null != ((InsertStatement) sqlStatement).getGeneratedKey()) {  
               processGeneratedKey(parameters, (InsertStatement) sqlStatement, result);  
           }  
           //路由  
           RoutingResult routingResult = route(parameters, sqlStatement);  
           //SQL重写引擎  
           SQLRewriteEngine rewriteEngine = new SQLRewriteEngine(shardingRule, logicSQL, databaseType, sqlStatement);  
           boolean isSingleRouting = routingResult.isSingleRouting();  
           // 处理分页  
           if (sqlStatement instanceof SelectStatement && null != ((SelectStatement) sqlStatement).getLimit()) {  
               processLimit(parameters, (SelectStatement) sqlStatement, isSingleRouting);  
           }  
           // SQL 重写  
           SQLBuilder sqlBuilder = rewriteEngine.rewrite(!isSingleRouting);  
           // 生成 ExecutionUnit  
           if (routingResult instanceof CartesianRoutingResult) {  
               for (CartesianDataSource cartesianDataSource : ((CartesianRoutingResult) routingResult).getRoutingDataSources()) {  
                   for (CartesianTableReference cartesianTableReference : cartesianDataSource.getRoutingTableReferences()) {  
                       result.getExecutionUnits().add(new SQLExecutionUnit(cartesianDataSource.getDataSource(), rewriteEngine.generateSQL(cartesianTableReference, sqlBuilder)));  
                   }  
               }  
           } else {  
               for (TableUnit each : routingResult.getTableUnits().getTableUnits()) {  
                   result.getExecutionUnits().add(new SQLExecutionUnit(each.getDataSourceName(), rewriteEngine.generateSQL(each, sqlBuilder)));  
               }  
           }  
           //打印sql  
           if (showSQL) {  
               SQLLogger.logSQL(logicSQL, sqlStatement, result.getExecutionUnits(), parameters);  
           }  
           return result;  
       }  
    路有结果:  routeResult  [SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_0        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_1        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_2        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_3        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_4        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_5        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_6        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_7        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_8        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_9        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_10        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_11        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_12        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_13        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_14        WHERE     user_id =?), SQLExecutionUnit(dataSource=demo_ds_0, sql=SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_15        WHERE     user_id =?)]


      放在更外层的大流程来看。ShardingPreparedStatement.execute()

    先route().再执行PreparedStatementExecutor.execute()

    private Collection<PreparedStatementUnit> route() throws SQLException {
            Collection<PreparedStatementUnit> result = new LinkedList<>();
            routeResult = routingEngine.route(getParameters());
            for (SQLExecutionUnit each : routeResult.getExecutionUnits()) {
                SQLType sqlType = routeResult.getSqlStatement().getType();
                Collection<PreparedStatement> preparedStatements;
                if (SQLType.DDL == sqlType) {
                    preparedStatements = generatePreparedStatementForDDL(each);
                } else {
                    preparedStatements = Collections.singletonList(generatePreparedStatement(each));
                }
                routedStatements.addAll(preparedStatements);
                for (PreparedStatement preparedStatement : preparedStatements) {
                    replaySetParameter(preparedStatement);
                    result.add(new PreparedStatementUnit(each, preparedStatement));
                }
            }
            return result;
        }

    结果举例:

      preparedStatements:[com.mysql.jdbc.JDBC4PreparedStatement@64b70919: SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_0        WHERE     user_id =** NOT SPECIFIED **]
     replaySetParameter:[com.mysql.jdbc.JDBC4PreparedStatement@64b70919: SELECT  AVG(order_id)       , COUNT(order_id) AS AVG_DERIVED_COUNT_0 , SUM(order_id) AS AVG_DERIVED_SUM_0 FROM        t_order_0        WHERE     user_id =101]
            

    参考:

    http://www.iocoder.cn/Sharding-JDBC/sql-rewrite/

    https://blog.csdn.net/feelwing1314/article/details/80404010

    展开全文
  • Android 记事本应用该应用是基于Google的sample notepad改写的,主要改进了操作UI以及对界面进行基本的美化。 在现有的功能上增加了加密功能。 启动app 启动app后,跳转次界面。 导航栏的3个按钮分别为“添加新...
  • PART 1 此部分主要体现java纯面对对象语言的语法与框架与给出源码中使用C++语言的异同。 主函数 给出源码的c++语言框架中,形式上主函数是脱离于类的,而在...改写后java主函数: 2.包 包是java中用于文件管理而 ...
  • 基于Jquery UI Dialog 的改写

    千次阅读 2015-06-09 16:42:24
    项目要使用一种类似于dialog的功能,让一部分页面能够弹出来,并且可以移动,当然使用dialog是很不错的选择 但是在真正使用的时候发现,jquery UI的dialog虽然能满足基本的要求但是,有些功能是不能满足,如果自己...
  • 改写NVMe历史的HMB技术解读

    千次阅读 2018-08-09 17:37:24
    现在无论是否安装由固态硬盘厂商提供的专用NVMe驱动程序,都无需再手动关闭写入高速缓冲区刷新选项,就能NVMe固态硬盘的性能得到100%的发挥。 用新功能、新提升来引诱用户升级,远比逼迫用户放弃旧有系统要好...
  • 其实最主要的是理解这个功能实现的思路,像游戏退出确认功能,你需要改写返回键的事件,把返回键原本的事件改成弹出一个Dialog,然后处理Dialog两个按钮的点击事件,比如取消按钮就直接把Dialog弹窗取消,确定就退出...
  • 摘要:《Python核心编程》第3...(2)练习代码的readout()函数有所改进,不再是逐行读取文件再关闭,而是在获取文件的所有内容后直接关闭文件,然后再从列表中读取文件的内容。 (3)新增功能使用情况:
  • 下面用一个小例子说明如何利用导演类、场景类完成重新开始游戏、暂停游戏、关闭游戏功能, 这个被改写的HelloWorld场景,一开始就声明一个在屏幕中央开始5s内上升100px的按钮精灵, 可以看到,点击左上角的...
  • 汇编中可以调用int 21h的0Ah功能 */ handle = _open(filename, 0); /* 打开文件, 返回句柄; 汇编对应调用: mov ah, 3Dh mov al, 0; 对应_open()的第2个参数, 表示只读方式 mov dx, offset filename int 21h...
  • //对应开启--enable 对应关闭--disable 具体可查看帮助 --enable-shared 是生成动态链接库 3、make编译 // 如果安装:路径对应configure --prefix 并make install 4、use gcc to generate ...
  • 因为ORM和JDBC的代码抽象程度不同导致了在不同层面执行改写工作量有较大差异,基于Mybatis的ORM层进行改写时工作量远小于基于Druid的JDBC层改写,因为JDBC更底层,要考虑的更多,例如执行模式是PreparedStatment还是...
  • 年度总结之三:底层驱动的改写

    千次阅读 2011-01-20 23:22:00
    来公司的第一项工作就是把引擎的图形驱动层由OpenGL改成Direct3D。   整改缘由:  1. 虽然市面上的大部分显卡... N显卡对OpenGL的某些特性是默认关闭的,需要使用NVemulate工具才能打开;
  • 4、IDEA默认快捷方式 1. Ctrl + Space  完成类、方法、变量名称的自动...跳转到指定的java文件(其它文件)这个功能很方便,至少我不用每回都在一长串的文件列表里找寻我想要编辑的类文件和jsp文件了  3. Ctrl
  • 王自如最近在测评中说了一句话:...1 IIS的asp支持不知道怎么安装,虽然印象是在控制面板->程序->添加windows功能里,但怎么也找不到!原因是在XP和win2k3里是在那地方,但在win2k8里要在Web服务器角色里添加该功能!这
  • 所以具体改动的代码以中期阶段项目里面的源码文件为准,项目源码下载请移步《改写《魔塔》中篇06:善后工作和注意事项(附:中期阶段项目下载)》。还有我们对原书中的项目进行了一些改动,包括用CCArray替换了...
  • 包括功能: •closeQuietly——这些关闭一个流的方法将忽略nulls和例外的情况。 •toXxx /read-这些方法从一个流读取数据。 • write这些方法将数据写到一个流里。 copy——这些方法用于从一个流的所有...
  • 这个是用vs2005写的,感觉那个关闭按钮图片支持不是那么好,在vs2008及其以上版里使用,效果更佳。    首先我们先新建一个项目,默认有个Form1窗体,将ShowIcon、ShowInTaskbar属性设置为False,这样这个窗体就...
  • Jmeter CSV Data Set Config 功能源码的修改
  • 无线默认关闭),这里不再详述。   测试一下njit-client吧!在putty之类的里面执行 njit-client username password interface #改成自己的用户名密码 试试吧!其中[interface]是接口,例如eth0,...
  • 随着技术发展,在进入Windows时代后,ScrollLock键的作用越来越小,不过在Excel中它还是有点用处:如果在ScrollLock关闭的状态下使用翻页键(如PageUp和PageDown)时,单元格选定区域会随之发生移动;反之,若要在滚动时...
  • 但我们的 MP3 制作好之后是从 NAND Flash 启动的,所以必需改写 U-Boot 代码使其具有从 Nand Flash 启动的功能。 S3C2440 的引脚 OM[1:0]控制系统的启动方式: (1)上电时如果 OM=10,处理器就从 ...
  • 1.要解决的问题  当由多个虚拟服务器提供web...通过简单地关闭过滤器,就可以很轻松地回到未使用CloudFront时的状态。 6.注意事项  当使用CloudFront时,由于内容缓存可能会出现删除或更新延迟的情况。
  • 临时切换数字键 按下【fn】或【shift】键不松手的同时按对应数字键即可。 长时间切换数字键 同时按下【fn】/【shift】+【Insert】...改写模式快捷键 大部分使用Word办公的人都熟知的功能。 按下【Insert】键,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 48,211
精华内容 19,284
关键字:

如何关闭改写功能