基于 C 的 SQL Parser 实现

一个函数,可以提取SQL语句中查询字段部分。虽然函数中使用了block,但是block可以很方便的转换成纯 c 的函数,而且未来 block 也很有可能成为 c 标准。

 

#import <Foundation/Foundation.h>

typedef struct Field {
    char name[63];
    char orignal_name[63];
}Field;

const Field EmptyField = {'\0', '\0'};

static inline BOOL SameField(Field field1, Field field2) {
    if (strcmp(field1.name, field2.name) == 0 && strcmp(field1.orignal_name, field2.orignal_name) == 0) {
        return YES;
    }
    return NO;
};

void sqlParser(const char *sqlCString) {
    __block NSInteger sqlCStringLength = strlen(sqlCString);
    __block NSInteger i = 0;
    __block char *tmpCString = malloc(sizeof(char) * (sqlCStringLength + 1));
    
    // 从位置 i 处开始截取字符串,直到遇到结束符位置,截取的字符串包括起始位置和终止位置
    // 截取结束,i 指向的是所截取的字符串后面第一个字符。
    __block __weak void (^split_string_duplicate)(char, NSInteger);
    void (^split_string)(char, NSInteger) = ^(char endChar, NSInteger start){
        NSInteger j = start;
        BOOL meetEnd = NO;
        switch (endChar) {
            case ',':
                // 对于逗号,单独列出来
                tmpCString[j++] = sqlCString[i++];
                tmpCString[j] = '\0';
                break;
            case ')':
                // 以右括号结尾,要屏蔽单引号和反单引号里的字符串
                while (i < sqlCStringLength) {
                    // 因为字符数组的长度比字符数组元素的个数少1,这里不用担心越界的问题
                    tmpCString[j++] = tolower(sqlCString[i++]);
                    if (meetEnd) {
                        tmpCString[j] = '\0';
                        break;
                    }
                    switch (sqlCString[i]) {
                        case '\'':
                            split_string_duplicate('\'', j);
                            j = strlen(tmpCString);
                            if (sqlCString[i] == ')') {
                                meetEnd = YES;
                            }
                            break;
                        case '`':
                            split_string_duplicate('`', j);
                            j = strlen(tmpCString);
                            if (sqlCString[i] == ')') {
                                meetEnd = YES;
                            }
                            break;
                        case ')':
                            meetEnd = YES;
                            break;
                        default:
                            break;
                    }
                }
                break;
            case '\'':
                // 单引号,要屏蔽两个连续的单引号
                while (i < sqlCStringLength) {
                    // 单引号的内容字符不转小写
                    tmpCString[j++] = sqlCString[i++];
                    if (meetEnd) {                      // 上一个字符是单引号
                        if (sqlCString[i] == '\'') {    // 又是单引号
                            meetEnd = NO;               // 继续
                        }else{                          // 不是单引号,说明字符结束了
                            tmpCString[j] = '\0';
                            break;
                        }
                    }else{
                        if (sqlCString[i] == '\'') {    // 下一个是单引号
                            meetEnd = YES;
                        }
                    }
                }
                break;
            case '`':
                // 反单引号 取所有内容
                while (i < sqlCStringLength) {
                    tmpCString[j++] = tolower(sqlCString[i++]);
                    if (meetEnd) {
                        tmpCString[j] = '\0';
                        break;
                    }else{
                        if (sqlCString[i] == '`') {
                            meetEnd = YES;
                        }
                    }
                }
                break;
            default:
                // 其它字符,遇到空格或逗号结束, 遇到左小括号就直到右小括号结束
                while (i < sqlCStringLength) {
                    tmpCString[j++] = tolower(sqlCString[i++]);
                    if (sqlCString[i] == '('){
                        split_string_duplicate(')', j);
                        j = strlen(tmpCString);
                    }
                    if (sqlCString[i] == ' ' || sqlCString[i] == ',') {
                        tmpCString[j] = '\0';
                        break;
                    }
                }
                break;
        }
    };
    // 复制上面这个block,使其可在其内部调用
    split_string_duplicate = split_string;
    
    Field fields[100] = {EmptyField};
    __block Field *fields_pointer = fields;    // 存放所有字段的数组
    __block char *tmp_field_name = NULL;         // 存放一个字段名
    __block BOOL meetAS = NO;           // 遇到 AS 关键字
    
    
    void (^field_parser)(char *) = ^(char *field_word) {
        if (field_word == NULL || strcmp(field_word, ",") == 0) {
            // 遇到逗号,或字段结束了,把临时字段名里的字符存入字段
            if (tmp_field_name != NULL) {
                if (meetAS) {
                    strcpy(fields_pointer->name, tmp_field_name);
                    meetAS = NO;
                }else{
                    strcpy(fields_pointer->orignal_name, tmp_field_name);
                }
                free(tmp_field_name);
                tmp_field_name = NULL;
                fields_pointer ++;
            }
        }else if(strcmp(field_word, "as") == 0){
            // 遇到 as 原始字段名结束
            if (tmp_field_name != NULL) {
                strcpy(fields_pointer->orignal_name, tmp_field_name);
                free(tmp_field_name);
                tmp_field_name = NULL;
            }
            meetAS = YES;
        }else{
            if (tmp_field_name == NULL) {
                tmp_field_name = malloc(sizeof(char) * (strlen(field_word) + 1));
                strcpy(tmp_field_name, field_word);
            }else{
                // 连续的单词
                tmp_field_name = realloc(tmp_field_name, (strlen(tmp_field_name) + 1 + strlen(field_word) + 1));
                strcat(tmp_field_name, " ");
                strcat(tmp_field_name, field_word);
            }
        }
    };
    BOOL meetField = NO;
    while (i < sqlCStringLength) {
    loop_start:
        switch (sqlCString[i]) {
            case '(':
                split_string(')', 0);
                break;
            case '\'':
                split_string('\'', 0);
                break;
            case '`':
                split_string('`', 0);
                break;
            case ' ':
                while (i++ < sqlCStringLength) {
                    if (sqlCString[i] != ' ') {
                        break;
                    }
                }
                goto loop_start;
                break;
            default:
                split_string(sqlCString[i], 0);
                break;
        }
        if (meetField == YES) {
            if (strlen(tmpCString)>0) {
                if (strcmp(tmpCString, "from") == 0) {
                    field_parser(NULL);
                    break;
                }
                field_parser(tmpCString);
            }
        }else if (strcmp(tmpCString, "select") == 0) {
            meetField = YES;
        }
        tmpCString[0] = '\0';
    }
    // 释放内存
    if (tmp_field_name != NULL) {
        free(tmp_field_name);
        tmp_field_name = NULL;
    }
    free(tmpCString);
    tmpCString = NULL;
    int fi = 0;
    while (!SameField(fields[fi], EmptyField)) {
        printf("name:%-20s, orignal_name:%-20s\n", fields[fi].name, fields[fi].orignal_name);
        fi ++;
    }
}

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQL解析器是一个用于解析和分析SQL查询语句的工具。它可以将SQL查询语句转换为语法树或解析树,以便后续进行语义分析、优化和执行。 在实现SQL解析器时,可以考虑以下步骤: 1. 词法分析:将输入的SQL查询语句分割成不同的词法单元,如关键字、标识符、运算符等。可以使用正则表达式或有限状态机来进行词法分析。 2. 语法分析:将词法单元组织成语法结构,形成语法树。可以使用上下文无关文法(Context-Free Grammar)或递归下降等算法进行语法分析。 3. 语义分析:对语法树进行分析,检查语义错误并进行修正。例如,检查表名和列名是否存在,检查数据类型是否匹配等。 4. 优化:对解析后的查询进行优化,以提高查询性能。这包括选择合适的查询计划、索引优化等。 5. 执行计划生成:根据优化后的查询计划,生成执行计划以执行实际的查询操作。 6. 执行查询:根据执行计划,执行查询并返回结果。 在实现SQL解析器时,可以使用一些开源工具或库来简化开发,如ANTLR、JSQLParser、Apache Calcite等。这些工具提供了丰富的API和语法规则,可以帮助您实现一个功能完善的SQL解析器。 请注意,SQL解析器的实现是一个复杂的任务,需要对SQL语法和数据库原理有一定的了解。如果您是初学者,建议先学习相关的数据库理论和SQL语法,然后再着手实现一个基本的SQL解析器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值