pike.git / lib / modules / Sql.pmod / tds.pike

version» Context lines:

pike.git/lib/modules/Sql.pmod/tds.pike:1:   /* -  * $Id: tds.pike,v 1.11 2006/02/14 10:20:34 grubba Exp $ +  * $Id: tds.pike,v 1.12 2006/02/14 16:19:34 grubba Exp $    *    * A Pike implementation of the TDS protocol.    *    * Henrik Grubbström 2006-02-08.    */      /* #define TDS_DEBUG */   /* #define TDS_CONVERT_DEBUG */      #ifdef TDS_DEBUG   #define TDS_WERROR(X...) werror("TDS:" + X)   #else   #define TDS_WERROR(X...)   #endif   #ifdef TDS_CONVERT_DEBUG   #define TDS_CONV_WERROR(X...) werror("TDS: Convert: " + X)   #else   #define TDS_CONV_WERROR(X...)   #endif    -  + //! + //! The TDS SQL-protocol. + //! + //! This protocol is used by Sybase and Microsoft's SQL-servers. + //! + //! @example + //! @tt{Sql.Sql con = Sql.Sql("tds://user:pass@@host/database");@} + //! + //! @seealso + //! @[Sql.Sql()] +    static int filter_noprint(int char)   {    return ((char == 32) || ((char != 0x7f) && (char & 0x60)))?char:'.';   }      static string hex_dump(string data) {    array(string) lines = data/16.0;    int off;    foreach(lines; int i; string line) {    array(array(int)) halves = (array(array(int)))((line + ("\0"*16))/8);
pike.git/lib/modules/Sql.pmod/tds.pike:40:   }      #if (__REAL_MAJOR__ > 7) || ((__REAL_MAJOR__ == 7) && (__REAL_MINOR__ >= 6))   // Static blocks affect nested classes in Pike 7.4.   // We don't want that...   static {   #endif /* Pike 7.6 or later */    constant DEF_MAJOR = 8;    constant DEF_MINOR = 0;    constant DEF_PORT = 1433; -  constant DEF_DOMAIN = ""; -  constant DEFAULT_CAPABILITIES = -  "\x01\x09\x00\x00\x06\x6d\x7f\xff" -  "\xff\xff\xfe\x02\x09\x00\x00\x00" -  "\x00\x0a\x68\x00\x00\x00"; +     constant TDS8VERSION = "\x01\x00\x00\x71";    constant CLIENTVERSION = "\x06\x83\xf2\xf8";    constant CONNECTIONID = "\0\0\0\0";    constant TIMEZONE = "\x88\xff\xff\xff";    constant COLLATION = "\x36\x04\x00\x00";    -  +  //! Tokens that may occur in the answers from the server.    enum Token {    TDS_ERROR = 3,    TDS_DONT_RETURN = 42,       TDS5_PARAMFMT2_TOKEN = 32, /* 0x20 */    TDS_LANGUAGE_TOKEN = 33, /* 0x21 TDS 5.0 only */    TDS_ORDERBY2_TOKEN = 34, /* 0x22 */    TDS_ROWFMT2_TOKEN = 97, /* 0x61 TDS 5.0 only */    TDS_LOGOUT_TOKEN = 113, /* 0x71 TDS 5.0 only? ct_close() */    TDS_RETURNSTATUS_TOKEN = 121, /* 0x79 */
pike.git/lib/modules/Sql.pmod/tds.pike:100:    TDS_DONEPROC_TOKEN = 254, /* 0xFE TDS_DONEPROC */    TDS_DONEINPROC_TOKEN = 255, /* 0xFF TDS_DONEINPROC */    /* CURSOR support: TDS 5.0 only*/    TDS_CURCLOSE_TOKEN = 128, /* 0x80 TDS 5.0 only */    TDS_CURFETCH_TOKEN = 130, /* 0x82 TDS 5.0 only */    TDS_CURINFO_TOKEN = 131, /* 0x83 TDS 5.0 only */    TDS_CUROPEN_TOKEN = 132, /* 0x84 TDS 5.0 only */    TDS_CURDECLARE_TOKEN = 134, /* 0x86 TDS 5.0 only */    };    +  //! Environment types. +  //! +  //! Used by @[TDS_ENV_CHANGE_TOKEN]    enum EnvType {    /* environment type field */    TDS_ENV_DATABASE = 1,    TDS_ENV_LANG = 2,    TDS_ENV_CHARSET = 3,    TDS_ENV_PACKSIZE = 4,    TDS_ENV_LCID = 5,    TDS_ENV_SQLCOLLATION = 7,    };    -  enum ColType { +  //! Field types. +  enum FieldType {    SYBBINARY = 45, /* 0x2d */    SYBBIT = 50, /* 0x32 */    SYBBITN = 104, /* 0x68 */    SYBCHAR = 47, /* 0x2f */    SYBDATETIME = 61, /* 0x3d */    SYBDATETIME4 = 58, /* 0x3a */    SYBDATETIMN = 111, /* 0x6f */    SYBDECIMAL = 106, /* 0x6a */    SYBFLT8 = 62, /* 0x3e */    SYBFLTN = 109, /* 0x6d */
pike.git/lib/modules/Sql.pmod/tds.pike:155:    XSYBBINARY = 173, /* 0xad */    XSYBCHAR = 175, /* 0xaf */    XSYBNCHAR = 239, /* 0xef */    XSYBNVARCHAR = 231, /* 0xe7 */    XSYBVARBINARY = 165, /* 0xa5 */    XSYBVARCHAR = 167, /* 0xa7 */       TDS_UT_TIMESTAMP = 80, /* User type. */    };    -  enum ResultTypes { -  TDS_ROW_RESULT = 4040, -  TDS_PARAM_RESULT = 4042, -  TDS_STATUS_RESULT = 4043, -  TDS_MSG_RESULT = 4044, -  TDS_COMPUTE_RESULT = 4045, -  TDS_CMD_DONE = 4046, -  TDS_CMD_SUCCEED = 4047, -  TDS_CMD_FAIL = 4048, -  TDS_ROWFMT_RESULT = 4049, -  TDS_COMPUTEFMT_RESULT = 4050, -  TDS_DESCRIBE_RESULT = 4051, -  TDS_DONE_RESULT = 4052, -  TDS_DONEPROC_RESULT = 4053, -  TDS_DONEINPROC_RESULT = 4054, -  }; -  +     string server_data;    string last_error;    -  +  //! Format and report an error.    void tds_error(string msg, mixed ... args)    {    if (sizeof(args)) msg = sprintf(msg, @args);    predef::error(last_error = msg);    }       static object utf16enc = Locale.Charset.encoder("UTF16LE");    static string string_to_utf16(string s)    {    return utf16enc->feed(s)->drain();    }    static object utf16dec = Locale.Charset.decoder("UTF16LE");    static string utf16_to_string(string s)    {    return utf16dec->feed(s)->drain();    }    -  +  //! A connection to a TDS server.    class Connection {    int major_version = DEF_MAJOR;    int minor_version = DEF_MINOR;    int port = DEF_PORT;    int block_size = 4096;    string server = "";    string server_charset = "";    string hostname = gethostname();    string appname = "";    string username = "";    string password = "";    string language = "";//"us_english";    string library_name = "TDS-Library";    string database = "";    string domain; -  string capabilities = DEFAULT_CAPABILITIES; +     -  +  //! The actual TCP connection.    Stdio.File socket;      #define FMT_SMALLINT "%-2c"   #define FMT_INT "%-4c"   #define FMT_INT8 "%-4c"    -  +  //! An incoming packet from the TDS server.    class InPacket    {    int inpos = 0;    string inbuf = "";    int done;       static void fill_buf()    {    if (done) {    TDS_WERROR("Filling buffer on finished packet!\n"
pike.git/lib/modules/Sql.pmod/tds.pike:337:    {    if (busy) {    tds_error("Creating InPacket on busy connection!\n");    }    busy = 1;    }    }       //static InPacket login_answer;    +  //! An outgoing packet to the TDS server.    class Packet    {    array(string|int) segments = ({});    array(string) strings = ({});    int flags;       static void create(int|void flags)    {    this_program::flags = flags;    }
pike.git/lib/modules/Sql.pmod/tds.pike:446:    }    }    string res = segments * "";    TDS_WERROR("Generated packet: %O (%d bytes)\n",    res, sizeof(res));    if (!flags) return res;    return sprintf("%-4c%s", sizeof(res)+4, res);    }    }    +  //! Send a packet to the TDS server. +  //! +  //! @note +  //! May only be called when the connection is idle. +  //! +  //! @returns +  //! If @[last] is true an @[InPacket] with the result +  //! will be returned.    InPacket send_packet(Packet p, int flag, int|void last)    {    if (busy) {    tds_error("Sending packet on busy connection!\n");    }       array(string) packets = ((string) p)/32768.0;    foreach(packets; int i; string raw) {    // NOTE: Network byteorder!!    raw = sprintf("%1c%1c%2c\0\0%1c\0%s",
pike.git/lib/modules/Sql.pmod/tds.pike:1356:    void Connect(string server, int port, string database,    string uid, string password)    {    con = Connection(server, port, database, uid, password);    }      #if (__REAL_MAJOR__ > 7) || ((__REAL_MAJOR__ == 7) && (__REAL_MINOR__ >= 6))   };   #endif /* Pike 7.6 or later */    +  + //! A compiled query.   class compile_query   {    int n_param;    array(string) splitted_query;       array(mixed) params;       void parse_prepared_query()    {    // FIXME:
pike.git/lib/modules/Sql.pmod/tds.pike:1424:    case '?':    res += ({ query[j..i-1] });    j = i+1;    break;    }    }    res += ({ query[j..] });    return map(res, string_to_utf16);    }    +  //! Compile a query. +  //! +  //! @seealso +  //! @[big_query()]    static void create(string query)    {    TDS_WERROR("Compiling query: %O\n", query);    splitted_query = split_query_on_placeholders(query);    n_param = sizeof(splitted_query)-1;    }   }    -  + //! A query result set.   class big_query   {    static int row_no;    static int eot;       static Connection.InPacket result_packet;    static array(mapping(string:mixed)) column_info;    -  +  //! Fetch the next row from the result set. +  //! +  //! @returns +  //! Returns @expr{0@} (zero) if all rows have been returned. +  //! +  //! Otherwise returns an @expr{array(string|int)@} with one +  //! entry for each field. If the field is @tt{NULL@} the +  //! entry will be @expr{0@} (zero), otherwise the entry +  //! will contain a string representing the value.    int|array(string|int) fetch_row()    {    if (eot) return 0;    TDS_WERROR("fetch_row()::::::::::::::::::::::::::::::::::::::::::\n");    int|array(string|int) res = con->process_row_tokens(result_packet,    column_info);    eot = !res;    row_no++;    return res;    }    -  +  //! Fetch a description of the fields in the result. +  //! +  //! @returns +  //! Returns an array with a mapping for each of the fields in +  //! the result. +  //! +  //! The mappings contain the following information: +  //! @ul +  //! @item +  //! Standard fields: +  //! @mapping +  //! @member string "name" +  //! The name of the field. +  //! @member string|void "table" +  //! The name of the table (if available). +  //! @endmapping +  //! @item +  //! TDS-specific fields: +  //! @mapping +  //! @member int(0..1) "nullable" +  //! @expr{1@} if the field may contain @tt{NULL@}. +  //! @member int(0..1) "writeable" +  //! @expr{1@} if the field may be changed. +  //! @member int(0..1) "identity" +  //! @expr{1@} if the field is the identity for the row. +  //! @member int "column_size" +  //! Width of the field. +  //! @member int(0..1) "timestamp" +  //! Time stamp information for last change is available. +  //! @member int|void "column_prec" +  //! Precision of the field. +  //! @member int|void "column_scale" +  //! Scale exponent of the field. +  //! @member int "usertype" +  //! @member int "flags" +  //! @member int "column_type" +  //! @member int "cardinal_type" +  //! @member int "varint_size" +  //! Internal use only. +  //! @endmapping +  //! @endul    array(mapping(string:mixed)) fetch_fields()    {    TDS_WERROR("fetch_fields()::::::::::::::::::::::::::::::::::::::::::\n");    return copy_value(column_info);    }    -  +  //! Execute a query against the database. +  //! +  //! @param query +  //! The query to execute. This can either be a string, or +  //! a compiled query. +  //! +  //! @seealso +  //! @[compile_query()]    static void create(string|compile_query query)    {    if (stringp(query)) {    query = compile_query(query);    }       query->parse_prepared_query();    if (busy) {    tds_error("Connection not idle.\n");    }
pike.git/lib/modules/Sql.pmod/tds.pike:1480:    result_packet = con->submit_execdirect(query, query->params);    }    column_info = con->process_result_tokens(result_packet);    if (!column_info) destruct();    }   }      static compile_query compiled_insert_id =    compile_query("SELECT @@identity AS insert_id");    + //! Fetch the identity of the last insert (if available). + //! + //! This performs the query @expr{"SELECT @@@@identity AS insert_id"@}. + //! + //! @returns + //! Returns the identity of the last insert as an integer if available. + //! Otherwise returns @expr{0@} (zero).   int insert_id()   {    object res = big_query(compiled_insert_id);    array(mapping(string:mixed)) field_info =    res->fetch_fields();    if (!field_info) return 0;    array(string|int) row = res->fetch_row();    if (!row) return 0;    foreach(field_info->name; int i; string name) {    if (name == "insert_id") return (int)row[i];    }    return 0;   }    -  + //! Return a string describing the server.   string server_info()   {    return server_data;   }    -  + //! Return the last error (or possibly the last warning or + //! informational message).   string error()   {    return last_error;   }    -  + //! Connect to a remote SQL server via the TDS protocol. + //! + //! @param server + //! Server to connect to. + //! + //! @param database + //! Database to connect to. + //! + //! @param user + //! User to access as. + //! + //! An explicit domain may be specified by preceeding the user name + //! with the domain name and a @expr{'\\'@}. + //! + //! @param password + //! Password to access with. + //! + //! Usually accessed via @[Sql.Sql()]. + //! + //! @seealso + //! @[Sql.Sql()]   static void create(string|void server, string|void database, -  string|void user, string|void pwd) +  string|void user, string|void password)   {    if (con) {    Disconnect();    }    int port = DEF_PORT;    if (server) {    array(string) tmp = server/":";    if (sizeof(tmp) > 1) {    port = (int)tmp[-1];    server = tmp[..sizeof(tmp)-2]*":";    }    } else {    server = "127.0.0.1";    }    Connect(server, port, database || "default", -  user || "", pwd || ""); +  user || "", password || "");   }